Linting
The lint package lexes and parses Scriptling source without executing it. It reports syntax errors from the parser; it does not perform semantic analysis, name resolution, style checks, or security analysis. Parsing untrusted text is safe because no script code runs, but a successful result does not make later execution safe.
Basic Linting
import "github.com/paularlott/scriptling/lint"
// Lint inline code
result := lint.Lint(`x = 1 + 2`, nil)
if result.HasIssues() {
fmt.Println(result.String())
}Linting Files
// Lint a single file
result, err := lint.LintFile("script.py")
if err != nil {
log.Fatal(err)
}
if result.HasErrors {
fmt.Println(result.String())
os.Exit(1)
}
// Lint multiple files
result, err = lint.LintFiles([]string{"a.py", "b.py", "c.py"})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Checked %d files, found %d issues\n",
result.FilesChecked, len(result.Errors))Lint Options
result := lint.Lint(code, &lint.Options{
Filename: "myscript.py", // Used for error messages
})LintError Type
Each issue found is represented as a LintError. The severity constants are part of the result schema, but the current parser-only implementation emits SeverityError with code parse-error; it does not currently produce warning or informational findings:
type LintError struct {
File string // Source file name (may be empty)
Line int // 1-based line number
Column int // 1-based column (0 if not available)
Message string // Human-readable description
Severity Severity // "error", "warning", or "info"
Code string // Optional error code
}Result Type
The Result type contains all findings:
type Result struct {
Errors []LintError // All issues found
FilesChecked int // Number of files linted
HasErrors bool // True if any severity="error" found
}Processing Lint Errors
result, _ := lint.LintFile("script.py")
for _, err := range result.Errors {
fmt.Printf("%s:%d: %s (%s)\n",
err.File, err.Line, err.Message, err.Severity)
// Check severity
if err.Severity == lint.SeverityError {
// Handle errors
} else if err.Severity == lint.SeverityWarning {
// Handle warnings
}
}
// Quick checks
if result.HasErrors {
fmt.Println("Script has syntax errors")
}
if result.HasIssues() {
fmt.Println("Script has lint issues")
}JSON Output
For integration with other tools, you can output results as JSON:
import "encoding/json"
result, _ := lint.LintFile("script.py")
output, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(output))Output:
{
"errors": [
{
"file": "script.py",
"line": 3,
"message": "expected token COLON",
"severity": "error",
"code": "parse-error"
}
],
"files_checked": 1,
"has_errors": true
}Use Cases
- Pre-execution syntax validation: Reject source that cannot be parsed
- CI/CD integration: Fail pipelines on syntax errors
- Editor integration: Report parser diagnostics without running code
- Untrusted-source parsing: Inspect syntax without executing the source
// Validate syntax, then execute under the host's normal runtime controls.
func validateAndExecute(p *scriptling.Scriptling, code string) (object.Object, error) {
result := lint.Lint(code, nil)
if result.HasErrors {
return nil, fmt.Errorf("script has syntax errors:\n%s", result.String())
}
// A successful parse does not establish that execution is safe.
return p.Eval(code)
}Complete Example
package main
import (
"fmt"
"log"
"os"
"github.com/paularlott/scriptling"
"github.com/paularlott/scriptling/lint"
"github.com/paularlott/scriptling/stdlib"
)
func main() {
// Script to validate and execute
script := `
def calculate(a, b):
return a + b
result = calculate(10, 20)
`
// Step 1: Lint the script
result := lint.Lint(script, &lint.Options{Filename: "calculator.py"})
if result.HasErrors {
fmt.Println("Script has syntax errors:")
for _, err := range result.Errors {
fmt.Printf(" %s:%d: %s\n", err.File, err.Line, err.Message)
}
os.Exit(1)
}
fmt.Printf("Lint passed: %d file(s) checked, %d issue(s) found\n",
result.FilesChecked, len(result.Errors))
// Step 2: Execute the script
p := scriptling.New()
stdlib.RegisterAll(p)
_, err := p.Eval(script)
if err != nil {
log.Fatal(err)
}
// Get the result
if val, err := p.GetVarAsInt("result"); err == nil {
fmt.Printf("Result: %d\n", val)
}
}