diff --git a/.gitignore b/.gitignore index 71b2ceb..8ea5372 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ *.so *.dylib +# Build artifacts +lambda + # Test binary, built with `go test -c` *.test diff --git a/.golangci.yml b/.golangci.yml index a9a6f07..a4a1c02 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,230 +1,83 @@ ---- -# golangci-lint configuration file made by @ccoVeille -# Source: https://github.com/ccoVeille/golangci-lint-config-examples/ -# Author: @ccoVeille -# License: MIT -# Variant: 03-safe -# Version: v2.0.0 -# version: "2" formatters: enable: - # format the code - gofmt - # format the block of imports - gci settings: - # format the code with Go standard library gofmt: - # simplify the code - # https://pkg.go.dev/cmd/gofmt#hdr-The_simplify_command simplify: true rewrite-rules: - # replace `interface{}` with `any` in the code on format - pattern: 'interface{}' replacement: 'any' - - # make sure imports are always in a deterministic order - # https://github.com/daixiang0/gci/ - gci: # define the section orders for imports + + gci: sections: - # Standard section: captures all standard packages. - standard - # Default section: catchall that is not standard or custom - default - # linters that related to local tool, so they should be separated - localmodule linters: exclusions: - # these presets where present in the v1 version of golangci-lint - # it's interesting to keep them when migrating, but removing them should be the goal presets: - # exclude check on comments format in godoc - # These are common false positives in poor code - # you should not use this on recent code you write from scratch - # More information: https://golangci-lint.run/usage/false-positives/#comments - # - # Please uncomment the following line if your code is not using the godoc format - comments - - # Common false positives - # feel free to remove this if you don't have any false positives - # More information: https://golangci-lint.run/usage/false-positives/#common-false-positives - common-false-positives - - # Legacy preset is not recommended anymore - # More information: https://golangci-lint.run/usage/false-positives/#legacy - legacy - - # std-error-handling is a set of rules that avoid reporting unhandled errors on common functions/methods - # More information: https://golangci-lint.run/usage/false-positives/#std-error-handling - std-error-handling - # some linters are enabled by default - # https://golangci-lint.run/usage/linters/ - # - # enable some extra linters enable: - # Errcheck is a program for checking for unchecked errors in Go code. - errcheck - - # Vet examines Go source code and reports suspicious constructs. - govet - - # Detects when assignments to existing variables are not used. - ineffassign - - # It's a set of rules from staticcheck. See https://staticcheck.io/ - staticcheck - - # Checks Go code for unused constants, variables, functions and types. - unused - - # Fast, configurable, extensible, flexible, and beautiful linter for Go. - # Drop-in replacement of golint. - revive - - # make sure to use t.Helper() when needed - thelper - - # mirror suggests rewrites to avoid unnecessary []byte/string conversion - mirror - - # detect the possibility to use variables/constants from the Go standard library. - usestdlibvars - - # Finds commonly misspelled English words. - misspell - - # Checks for duplicate words in the source code. - dupword - - # linter to detect errors invalid key values count - loggercheck - - # detect when a package or method could be replaced by one from the standard library - exptostd - - # detects nested contexts in loops or function literals - fatcontext - - # Reports uses of functions with replacement inside the testing package. - usetesting settings: revive: rules: - # these are the default revive rules - # you can remove the whole "rules" node if you want - # BUT - # ! /!\ they all need to be present when you want to add more rules than the default ones - # otherwise, you won't have the default rules, but only the ones you define in the "rules" node - - # Blank import should be only in a main or test package, or have a comment justifying it. - name: blank-imports - - # context.Context() should be the first parameter of a function when provided as argument. - name: context-as-argument arguments: - allowTypesBefore: "*testing.T" - - # Basic types should not be used as a key in `context.WithValue` - name: context-keys-type - - # Importing with `.` makes the programs much harder to understand - name: dot-imports - - # Empty blocks make code less readable and could be a symptom of a bug or unfinished refactoring. - name: empty-block - - # for better readability, variables of type `error` must be named with the prefix `err`. - name: error-naming - - # for better readability, the errors should be last in the list of returned values by a function. - name: error-return - - # for better readability, error messages should not be capitalized or end with punctuation or a newline. - name: error-strings - - # report when replacing `errors.New(fmt.Sprintf())` with `fmt.Errorf()` is possible - name: errorf - - # check naming and commenting conventions on exported symbols. - name: exported arguments: - # make error messages clearer - "sayRepetitiveInsteadOfStutters" - - # incrementing an integer variable by 1 is recommended to be done using the `++` operator - name: increment-decrement - - # highlights redundant else-blocks that can be eliminated from the code - # - name: indent-error-flow - - # This rule suggests a shorter way of writing ranges that do not use the second value. - name: range - - # receiver names in a method should reflect the struct name (p for Person, for example) - name: receiver-naming - - # redefining built in names (true, false, append, make) can lead to bugs very difficult to detect. - name: redefines-builtin-id - - # redundant else-blocks that can be eliminated from the code. - # - name: superfluous-else - - # prevent confusing name for variables when using `time` package - name: time-naming - - # warns when an exported function or method returns a value of an un-exported type. - name: unexported-return - - # spots and proposes to remove unreachable code. also helps to spot errors - name: unreachable-code - - # Functions or methods with unused parameters can be a symptom of an unfinished refactoring or a bug. - name: unused-parameter - - # report when a variable declaration can be simplified - name: var-declaration - - # warns when initialism, variable or package naming conventions are not followed. - name: var-naming misspell: - # Correct spellings using locale preferences for US or UK. - # Setting locale to US will correct the British spelling of 'colour' to 'color'. - # Default ("") is to use a neutral variety of English. locale: US - - # List of words to ignore - # among the one defined in https://github.com/golangci/misspell/blob/master/words.go ignore-rules: [] - # - valor - # - and - - # Extra word corrections. extra-words: [] - # - typo: "whattever" - # correction: "whatever" output: - # Order to use when sorting results. - # Possible values: `file`, `linter`, and `severity`. - # - # If the severity values are inside the following list, they are ordered in this order: - # 1. error - # 2. warning - # 3. high - # 4. medium - # 5. low - # Either they are sorted alphabetically. - # - # Default: ["file"] sort-order: - linter - severity - - file # filepath, line, and column. \ No newline at end of file + - file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..082b194 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "makefile.configureOnOpen": false +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7bf1221 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,42 @@ +# Guide To `lambda` + +## Coding Styles + +### Conventional Commits + +Use conventional commit format: `: `. + +**Types**: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf` + +**Examples**: + +- `feat: add explanation mode flag to CLI` +- `fix: correct variable renaming in nested abstractions` +- `docs: update Makefile documentation` + +### Branch Names + +Use format: `/` with kebab-case. + +**Types**: Same as commits: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf` + +**Examples**: + +- `feat/explanation-mode` +- `fix/variable-renaming` +- `docs/makefile-improvements` +- `refactor/silent-directive` + +## Pull Request Management + +Use the `tea` CLI (Gitea command-line tool) for PR operations instead of `gh`. + +**Common commands**: + +- `tea pr create` - Create a new pull request +- `tea pr list` - List pull requests +- `tea pr checkout ` - Check out a PR locally +- `tea pr close ` - Close a pull request +- `tea pr merge ` - Merge a pull request + +**Creating PRs**: Always create PRs to the `main` branch unless specified otherwise diff --git a/Makefile b/Makefile index 42c6777..fe7f1b9 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,40 @@ -BINARY_NAME=lambda.exe - -it: - @ go build -o ${BINARY_NAME} ./cmd/lambda - @ chmod +x ${BINARY_NAME} - +BINARY_NAME=lambda TEST=simple +.DEFAULT_GOAL := help +.SILENT: +.PHONY: help it run profile explain graph clean + +help: + echo "Available targets:" + echo " it - Build the lambda binary" + echo " run - Build and run with sample input (default: simple.txt)" + echo " profile - Build and run with CPU profiling enabled" + echo " explain - Run with explanation mode and profiling" + echo " graph - Generate CPU profile visualization" + echo " clean - Remove build artifacts" + echo "" + echo "Usage: make run TEST=" + +it: + go build -o ${BINARY_NAME} ./cmd/lambda + run: it - @ ./lambda.exe - < ./samples/$(TEST).txt > program.out + ./lambda - < ./samples/$(TEST).txt > program.out profile: it - @ ./lambda.exe -p profile/cpu.prof - < ./samples/$(TEST).txt > program.out + mkdir -p profile + ./lambda -p profile/cpu.prof - < ./samples/$(TEST).txt > program.out explain: it - @ ./lambda.exe -x -p profile/cpu.prof - < ./samples/$(TEST).txt > program.out + mkdir -p profile + ./lambda -x -p profile/cpu.prof - < ./samples/$(TEST).txt > program.out -graph: - @ go tool pprof -raw -output=profile/cpu.raw profile/cpu.prof - @ go tool pprof -svg profile/cpu.prof > profile/cpu.svg - @ open profile/cpu.svg +graph: profile + go tool pprof -raw -output=profile/cpu.raw profile/cpu.prof + go tool pprof -svg profile/cpu.prof > profile/cpu.svg + echo "Profile graph saved to 'file://profile/cpu.svg'" + +clean: + rm -f ${BINARY_NAME} program.out + rm -rf profile/ diff --git a/makefile-improvements.md b/makefile-improvements.md new file mode 100644 index 0000000..72ee51c --- /dev/null +++ b/makefile-improvements.md @@ -0,0 +1,114 @@ +# Makefile Improvements + +This document lists the issues identified in the original Makefile and the improvements that were implemented. + +## Issues Fixed + +### 1. Hardcoded `.exe` extension on Unix +**Problem**: `BINARY_NAME=lambda.exe` used a Windows extension on macOS/Linux systems. + +**Solution**: Changed to `BINARY_NAME=lambda` since Unix executables don't use extensions. + +**Commit**: `0d06fac` - fix: remove Windows .exe extension from binary name + +--- + +### 2. Redundant `chmod +x` +**Problem**: The `chmod +x` command was unnecessary since `go build` already sets the executable bit. + +**Solution**: Removed the redundant `chmod +x ${BINARY_NAME}` line. + +**Commit**: `e0b0b92` - refactor: remove redundant chmod +x command + +--- + +### 3. Missing `.PHONY` declarations +**Problem**: Without `.PHONY`, if files named `run`, `graph`, etc. existed, Make would skip those targets. + +**Solution**: Added `.PHONY: help it run profile explain graph clean` declaration. + +**Commit**: `e5ceeb2` - feat: add .PHONY declarations for all targets + +--- + +### 4. No `clean` target +**Problem**: No standard way to remove build artifacts. + +**Solution**: Added `clean` target to remove binary, output files, and profile directory: +```makefile +clean: + rm -f ${BINARY_NAME} program.out + rm -rf profile/ +``` + +**Commit**: `7927df4` - feat: add clean target to remove build artifacts + +--- + +### 5. Missing `help` or default target +**Problem**: Running `make` with no arguments was unclear about available targets. + +**Solution**: Added `help` target documenting all available commands and their usage. + +**Commit**: `24fdc1c` - feat: add help target to document available commands + +--- + +### 6. Profile directory not created +**Problem**: The `profile` and `explain` targets wrote to `profile/cpu.prof` but never created the directory, causing failures on first run. + +**Solution**: Added `mkdir -p profile` to both `profile` and `explain` targets. + +**Commit**: `bb48d07` - fix: ensure profile directory exists before writing + +--- + +### 7. No dependency check on `graph` +**Problem**: The `graph` target assumed `profile/cpu.prof` exists but didn't depend on `profile`. + +**Solution**: Changed `graph:` to `graph: profile` to ensure profiling runs first. + +**Commit**: `3158c35` - fix: add profile dependency to graph target + +--- + +### 8. Verbose command prefixes +**Problem**: Every command used `@` prefix individually to suppress output, cluttering the file. + +**Solution**: Added `.SILENT:` directive at the top and removed all `@` prefixes. Also moved `TEST` variable to top with other variables. + +**Commit**: `8f70bfb` - refactor: use .SILENT directive instead of @ prefixes + +--- + +## Additional Suggestions (Not Yet Implemented) + +### 9. Missing `lambda` binary in `.gitignore` +**Issue**: The `.gitignore` has `*.exe` but not the actual `lambda` binary name. Since we removed the `.exe` extension, the binary won't be ignored. + +**Recommendation**: Add `lambda` to `.gitignore`: +``` +# Build artifacts +lambda +``` + +--- + +### 10. No explicit default target +**Issue**: While `help` is currently the first target (and thus default), it's not explicitly declared. + +**Recommendation**: Add `.DEFAULT_GOAL = help` at the top for clarity: +```makefile +BINARY_NAME=lambda +TEST=simple + +.DEFAULT_GOAL := help +.SILENT: +.PHONY: help it run profile explain graph clean +``` + +--- + +## Summary + +The Makefile has been significantly improved with better organization, proper dependency management, directory creation, helpful documentation, and cleaner syntax. The remaining suggestions are minor quality-of-life improvements that can be addressed as needed. diff --git a/pkg/deltanet/node.go b/pkg/deltanet/node.go index 91a77d0..bdf99cd 100644 --- a/pkg/deltanet/node.go +++ b/pkg/deltanet/node.go @@ -1,7 +1,5 @@ package deltanet -/** ------------------------------------------------------------------------- */ - // A connection between exactly two nodes in a graph. type Edge struct { A, B Node @@ -28,8 +26,6 @@ func (e Edge) IsPrincipleEdge() bool { return e.A.GetMainPort() == e && e.B.GetMainPort() == e } -/** ------------------------------------------------------------------------- */ - type Node interface { // Returns the principle port that the node is attached to. GetMainPort() Edge @@ -42,8 +38,6 @@ type Node interface { GetLabel() string } -/** ------------------------------------------------------------------------- */ - type EraserNode struct { Main Edge } @@ -52,8 +46,6 @@ func (n EraserNode) GetLabel() string { return "Ⓧ" } func (n EraserNode) GetMainPort() Edge { return n.Main } func (n EraserNode) GetAuxPorts() []Edge { return []Edge{} } -/** ------------------------------------------------------------------------- */ - type ReplicatorNode struct { Main Edge Level uint @@ -68,8 +60,6 @@ func (n ReplicatorNode) GetAuxPorts() []Edge { return n.Aux } // Returns the level of the replicator node. func (n ReplicatorNode) GetLevel() uint { return n.Level } -/** ------------------------------------------------------------------------- */ - type FanNode struct { Label string Main Edge @@ -80,8 +70,6 @@ func (n FanNode) GetLabel() string { return n.Label } func (n FanNode) GetMainPort() Edge { return n.Main } func (n FanNode) GetAuxPorts() []Edge { return []Edge{n.Left, n.Right} } -/** ------------------------------------------------------------------------- */ - type TerminalNode struct { Label string Main Edge @@ -90,5 +78,3 @@ type TerminalNode struct { func (n TerminalNode) GetLabel() string { return n.Label } func (n TerminalNode) GetMainPort() Edge { return n.Main } func (n TerminalNode) GetAuxPorts() []Edge { return []Edge{} } - -/** ------------------------------------------------------------------------- */ diff --git a/pkg/lambda/expression.go b/pkg/lambda/expression.go index e2814b2..e96090d 100644 --- a/pkg/lambda/expression.go +++ b/pkg/lambda/expression.go @@ -5,8 +5,6 @@ type Expression interface { Copy() Expression } -/** ------------------------------------------------------------------------- */ - type Abstraction struct { Parameter string Body Expression @@ -24,8 +22,6 @@ func NewAbstraction(parameter string, body Expression) *Abstraction { return &Abstraction{Parameter: parameter, Body: body} } -/** ------------------------------------------------------------------------- */ - type Application struct { Abstraction Expression Argument Expression @@ -43,8 +39,6 @@ func NewApplication(function Expression, argument Expression) *Application { return &Application{Abstraction: function, Argument: argument} } -/** ------------------------------------------------------------------------- */ - type Variable struct { Value string } @@ -61,8 +55,6 @@ func NewVariable(name string) *Variable { return &Variable{Value: name} } -/** ------------------------------------------------------------------------- */ - type Visitor interface { VisitAbstraction(*Abstraction) VisitApplication(*Application) diff --git a/pkg/saccharine/expression.go b/pkg/saccharine/expression.go index 80c8f1b..fb4e7f8 100644 --- a/pkg/saccharine/expression.go +++ b/pkg/saccharine/expression.go @@ -4,8 +4,6 @@ type Expression interface { IsExpression() } -/** ------------------------------------------------------------------------- */ - type Abstraction struct { Parameters []string Body Expression @@ -30,8 +28,6 @@ func (Application) IsExpression() {} func (Atom) IsExpression() {} func (Clause) IsExpression() {} -/** ------------------------------------------------------------------------- */ - func NewAbstraction(parameter []string, body Expression) *Abstraction { return &Abstraction{Parameters: parameter, Body: body} } diff --git a/pkg/saccharine/statement.go b/pkg/saccharine/statement.go index 1d0dd15..2a5b938 100644 --- a/pkg/saccharine/statement.go +++ b/pkg/saccharine/statement.go @@ -4,8 +4,6 @@ type Statement interface { IsStatement() } -/** ------------------------------------------------------------------------- */ - type LetStatement struct { Name string Parameters []string @@ -19,8 +17,6 @@ type DeclareStatement struct { func (LetStatement) IsStatement() {} func (DeclareStatement) IsStatement() {} -/** ------------------------------------------------------------------------- */ - func NewLet(name string, parameters []string, body Expression) *LetStatement { return &LetStatement{Name: name, Parameters: parameters, Body: body} }