{"id":16862329,"url":"https://github.com/dave/brenda","last_synced_at":"2025-03-22T06:32:01.589Z","repository":{"id":57482930,"uuid":"84796756","full_name":"dave/brenda","owner":"dave","description":"Brenda is a boolean expression solver for Go AST","archived":false,"fork":false,"pushed_at":"2019-02-06T08:31:18.000Z","size":23,"stargazers_count":46,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-18T09:04:39.625Z","etag":null,"topics":["ast","boolean","boolean-algebra","boolean-expression","boolean-formulas","boolean-operations","boolean-satisfiability","go","golang"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dave.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-13T07:31:01.000Z","updated_at":"2024-06-27T06:22:06.000Z","dependencies_parsed_at":"2022-08-27T20:02:40.968Z","dependency_job_id":null,"html_url":"https://github.com/dave/brenda","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dave%2Fbrenda","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dave%2Fbrenda/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dave%2Fbrenda/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dave%2Fbrenda/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dave","download_url":"https://codeload.github.com/dave/brenda/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244918499,"owners_count":20531683,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","boolean","boolean-algebra","boolean-expression","boolean-formulas","boolean-operations","boolean-satisfiability","go","golang"],"created_at":"2024-10-13T14:35:12.538Z","updated_at":"2025-03-22T06:31:56.579Z","avatar_url":"https://github.com/dave.png","language":"Go","readme":"[![Build Status](https://travis-ci.org/dave/brenda.svg?branch=master)](https://travis-ci.org/dave/brenda) [![Go Report Card](https://goreportcard.com/badge/github.com/dave/brenda)](https://goreportcard.com/report/github.com/dave/brenda) [![codecov](https://codecov.io/gh/dave/brenda/branch/master/graph/badge.svg)](https://codecov.io/gh/dave/brenda)\n\n# Brenda\n\nBrenda is a boolean expression solver.\n\nGiven an AST expression containing an arbitrary combination of `!`, `\u0026\u0026` \nand `||` expressions, it is possible to solve the boolean state of certain \ncomponents. For example:\n\n```go\nprintExample(`\n\tvar a bool\n\tif a {} else {}\n`)\n// Output:\n// if a {\n// \t// a TRUE\n// } else {\n// \t// a FALSE\n// }\n```\n\nSome inputs may be unknown:\n\n```go\nprintExample(`\n\tvar a, b, c bool\n\tif a \u0026\u0026 (b || c) {} else if b {}\n`)\n// Output:\n// if a \u0026\u0026 (b || c) {\n// \t// a TRUE\n// \t// b UNKNOWN\n// \t// c UNKNOWN\n// } else if b {\n// \t// a FALSE\n// \t// b TRUE\n// \t// c UNKNOWN\n// }\n```\n\nSome branches may be impossible:\n\n```go\nprintExample(`\n\tvar a bool\n\tif a {} else if !a {} else {}\n`)\n// Output:\n// if a {\n// \t// a TRUE\n// } else if !a {\n// \t// a FALSE\n// } else {\n// \t// IMPOSSIBLE\n// }\n```\n\nBrenda supports complex components, and can detect the inverse use of `==`, `!=`, \n`\u003c`, `\u003e=`, `\u003e` and `\u003c=`:\n\n```go\nprintExample(`\n\tvar a error\n\tvar b, c bool\n\tvar d int\n\tif a == nil \u0026\u0026 (b \u0026\u0026 d \u003e 0) || c {} else if d \u003c= 0 || c {} else if b {}\n`)\n// Output:\n// if a == nil \u0026\u0026 (b \u0026\u0026 d \u003e 0) || c {\n// \t// a == nil UNKNOWN\n// \t// b UNKNOWN\n// \t// c UNKNOWN\n// \t// d \u003e 0 UNKNOWN\n// } else if d \u003c= 0 || c {\n// \t// a == nil UNKNOWN\n// \t// b UNKNOWN\n// \t// c FALSE\n// \t// d \u003c= 0 TRUE\n// } else if b {\n// \t// a == nil FALSE\n// \t// b TRUE\n// \t// c FALSE\n// \t// d \u003e 0 TRUE\n// }\n```\n\nHere's an example of the full usage:\n\n```go\n// A simple source file\nsrc := `package foo\n\nfunc foo(a, b bool) {\n\tif a { } else if b { } else { }\n}`\n\n// We parse the AST\nfset := token.NewFileSet()\nf, err := parser.ParseFile(fset, \"foo.go\", src, 0)\nif err != nil {\n\tfmt.Println(err)\n\treturn\n}\n\n// We extract type info\ninfo := \u0026types.Info{Uses: make(map[*ast.Ident]types.Object)}\nconf := types.Config{Importer: importer.Default()}\nif _, err = conf.Check(\"foo\", fset, []*ast.File{f}, info); err != nil {\n\tfmt.Println(err)\n\treturn\n}\n\n// Walk the AST until we find the first *ast.IfStmt\nvar ifs *ast.IfStmt\nast.Inspect(f, func(node ast.Node) bool {\n\tif ifs != nil {\n\t\treturn false\n\t}\n\tif n, ok := node.(*ast.IfStmt); ok \u0026\u0026 n != nil {\n\t\tifs = n\n\t\treturn false\n\t}\n\treturn true\n})\nif ifs == nil {\n\tfmt.Println(\"No *ast.IfStmt found\")\n\treturn\n}\n\nvar printIf func(*ast.IfStmt, ...ast.Expr) error\nvar sprintResults func(*brenda.Solver) string\nvar sprintNode func(ast.Node) string\n\n// This is called recursively for the if and all else-if statements. falseExpr\n// is a slice of all the conditions that came before an else-if statement,\n// which must all be false for the else-if to be reached.\nprintIf = func(ifStmt *ast.IfStmt, falseExpr ...ast.Expr) error {\n\n\ts := brenda.NewSolver(fset, info.Uses, ifStmt.Cond, falseExpr...)\n\terr := s.SolveTrue()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Printf(\"if %s {\\n%s\\n}\", sprintNode(ifStmt.Cond), sprintResults(s))\n\n\tswitch e := ifStmt.Else.(type) {\n\tcase *ast.BlockStmt:\n\n\t\t// Else block\n\t\ts := brenda.NewSolver(fset, info.Uses, ifStmt.Cond, falseExpr...)\n\t\ts.SolveFalse()\n\n\t\tfmt.Printf(\" else {\\n%s\\n}\", sprintResults(s))\n\n\tcase *ast.IfStmt:\n\n\t\t// Else if statement\n\t\tfmt.Print(\" else \")\n\n\t\t// Add the condition from the current if statement to the list of\n\t\t// false expressions for the else-if solver\n\t\tfalseExpr = append(falseExpr, ifStmt.Cond)\n\t\tprintIf(e, falseExpr...)\n\n\t}\n\treturn nil\n}\n\n// Helper function to print results\nsprintResults = func(s *brenda.Solver) string {\n\tif s.Impossible {\n\t\t// If the expression is impossible\n\t\treturn \"\\t// IMPOSSIBLE\"\n\t}\n\n\t// The results must be sorted to ensure repeatable output\n\tvar lines []string\n\tfor expr, result := range s.Components {\n\t\tswitch {\n\t\tcase result.Match:\n\t\t\tlines = append(lines, fmt.Sprint(\"\\t// \", printNode(fset, expr), \" TRUE\"))\n\t\tcase result.Inverse:\n\t\t\tlines = append(lines, fmt.Sprint(\"\\t// \", printNode(fset, expr), \" FALSE\"))\n\t\tdefault:\n\t\t\tlines = append(lines, fmt.Sprint(\"\\t// \", printNode(fset, expr), \" UNKNOWN\"))\n\t\t}\n\t}\n\tsort.Strings(lines)\n\treturn strings.Join(lines, \"\\n\")\n}\n\n// Helper function to print AST nodes\nsprintNode = func(n ast.Node) string {\n\tbuf := \u0026bytes.Buffer{}\n\terr := format.Node(buf, fset, n)\n\tif err != nil {\n\t\treturn err.Error()\n\t}\n\treturn buf.String()\n}\n\nif err := printIf(ifs); err != nil {\n\tfmt.Println(err)\n\treturn\n}\n\n// Output:\n// if a {\n// \t// a TRUE\n// } else if b {\n// \t// a FALSE\n// \t// b TRUE\n// } else {\n// \t// a FALSE\n// \t// b FALSE\n// }\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdave%2Fbrenda","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdave%2Fbrenda","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdave%2Fbrenda/lists"}