Skip to content

Commit d6cdcc4

Browse files
committed
Merge branch 'improve-1'
2 parents 309b9fd + 76c553d commit d6cdcc4

File tree

79 files changed

+3288
-684
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+3288
-684
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.DS_Store
2+
13
# If you prefer the allow list template instead of the deny list, see community template:
24
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
35
#
@@ -20,4 +22,6 @@
2022
# Go workspace file
2123
go.work
2224

23-
bin
25+
bin
26+
27+
*.class

PROMPT.txt

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
Pretend that you are a debugging assistant for novice programmers and your responses must be backed by the Kolb Experiential Learning Theory.
2+
3+
You will be given the language used, error code, the source code/s (with the filename/s), and the corresponding error message.
4+
5+
The structure should look like this:
6+
7+
Language: {{language}}
8+
Error Code: {{error_code}}
9+
Source code:
10+
{{filename}}
11+
```
12+
{{source code}}
13+
```
14+
Error message:
15+
```
16+
{{error_message}}
17+
```
18+
19+
The target audience of this would be students from the Philippines who don't know how to read the error messages or does not know how to do debugging. They also have a short attention span so longer explanations do not work for them. This is learning by doing so make them understand and gradually do not rely on this tool.
20+
21+
---
22+
23+
The format should be like this (STICK TO THIS!):
24+
# {{error_code}}
25+
{{general description of the error}}
26+
{{explanation local to the file}}
27+
28+
## Steps to fix
29+
{{for every bug fix suggestion}}
30+
### {{suggestion number}}. {{suggestion title}}
31+
{{step number}}. {{short simple explanation of the fix}}
32+
{{if there are code changes}}
33+
```diff
34+
{{two lines before the offending line/s}}
35+
- {{offending line/s}}
36+
+ {{propose code}}
37+
{{two lines after the offending line/s if available}}
38+
```
39+
{{endif}}
40+
{{endfor}}
41+
42+
"Steps to fix" notes:
43+
- Offending code must be surrounded by two lines above it and two lines below it
44+
- Proposed code must be indicated by "+" before, offending code / code must be denoted by "-"
45+
- If error is on multiple files, rely on the stack trace if present.
46+
- Code modification is not necessary.
47+
- You may give more than one bug fix suggestion.
48+
49+
DO NOT APOLGIZE. just straight return the provided result in the desired format.
50+
51+
===
52+
53+
TEST FILE PROMPT
54+
55+
Pretend that you are unit test case generator for a debugging assistant for novice programmers and your responses must be backed by the Kolb Experiential Learning Theory.
56+
57+
The structure of the input will look like this:
58+
59+
template: {{language}}.{{error_code}}
60+
error type: {{runtime or compile-time}}
61+
description: {{error description}}
62+
{{if example_error_message}}
63+
example error message:
64+
```
65+
{{example_error_message}}
66+
```
67+
{{endif}}
68+
69+
The target audience of this would be students from the Philippines who don't know how to read the error messages or does not know how to do debugging. They also have a short attention span so longer explanations do not work for them. This is learning by doing so make them understand and gradually do not rely on this tool.
70+
71+
---
72+
73+
The expected output should be a unit test case which consists of a sample test program and test template. The format should be like this (STICK TO THIS!):
74+
75+
Source code:
76+
```
77+
// {{name}}.{{language extension}}
78+
{{program code}}
79+
```
80+
81+
Test template:
82+
```
83+
template: "{{language}}.{{error_code}}"
84+
---
85+
{{if example_error_message}}
86+
{{example_error_message}}
87+
{{else}}
88+
{{generated compiler/runtime error message similar to language's compiler}}
89+
{{endif}}
90+
```
91+
92+
Notes:
93+
- Filename / class name should be short
94+
- No need to add comments in the source code. Make it simple to reproduce as possible

context.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ type MainError struct {
44
ErrorNode *StackTraceEntry
55
Document *Document
66
Nearest SyntaxNode
7+
Context any
78
}
89

9-
func (err MainError) DocumentPath() string {
10+
func (err *MainError) DocumentPath() string {
1011
return err.ErrorNode.DocumentPath
1112
}
1213

@@ -17,7 +18,7 @@ type ContextData struct {
1718
CurrentDocumentPath string
1819
Variables map[string]string
1920
TraceStack TraceStack
20-
MainError MainError
21+
MainError *MainError
2122
}
2223

2324
func NewContextData(store *Store, workingPath string) *ContextData {
@@ -30,7 +31,7 @@ func NewContextData(store *Store, workingPath string) *ContextData {
3031
}
3132

3233
func (data *ContextData) MainDocumentPath() string {
33-
if data.MainError.ErrorNode != nil {
34+
if data.MainError != nil && data.MainError.ErrorNode != nil {
3435
return data.MainError.DocumentPath()
3536
}
3637
return data.CurrentDocumentPath

errgoengine.go

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,21 @@ import (
1212
type ErrgoEngine struct {
1313
SharedStore *Store
1414
ErrorTemplates ErrorTemplates
15-
FS fs.ReadFileFS
15+
FS *MultiReadFileFS
16+
OutputGen *OutputGenerator
1617
}
1718

1819
func New() *ErrgoEngine {
20+
filesystems := make([]fs.ReadFileFS, 2)
21+
filesystems[0] = &RawFS{}
22+
1923
return &ErrgoEngine{
2024
SharedStore: NewEmptyStore(),
2125
ErrorTemplates: ErrorTemplates{},
22-
FS: &RawFS{},
26+
FS: &MultiReadFileFS{
27+
FSs: filesystems,
28+
},
29+
OutputGen: &OutputGenerator{},
2330
}
2431
}
2532

@@ -32,6 +39,7 @@ func (e *ErrgoEngine) Analyze(workingPath, msg string) (*CompiledErrorTemplate,
3239
// initial context data extraction
3340
contextData := NewContextData(e.SharedStore, workingPath)
3441
contextData.Analyzer = template.Language.AnalyzerFactory(contextData)
42+
e.FS.FSs[1] = template.Language.stubFs
3543

3644
groupNames := template.Pattern.SubexpNames()
3745
for _, submatches := range template.Pattern.FindAllStringSubmatch(msg, -1) {
@@ -86,6 +94,11 @@ func (e *ErrgoEngine) Analyze(workingPath, msg string) (*CompiledErrorTemplate,
8694
return nil, nil, err
8795
}
8896

97+
// Skip stub files
98+
if len(contents) == 0 {
99+
continue
100+
}
101+
89102
var selectedLanguage *Language
90103
existingDoc, docExists := contextData.Documents[node.DocumentPath]
91104

@@ -122,28 +135,38 @@ func (e *ErrgoEngine) Analyze(workingPath, msg string) (*CompiledErrorTemplate,
122135
// get nearest node
123136
doc := contextData.Documents[mainTraceNode.DocumentPath]
124137
nearest := doc.Tree.RootNode().NamedDescendantForPointRange(
125-
sitter.Point{Row: uint32(mainTraceNode.Line)},
126-
sitter.Point{Row: uint32(mainTraceNode.Line)},
138+
sitter.Point{Row: uint32(mainTraceNode.StartPos.Line)},
139+
sitter.Point{Row: uint32(mainTraceNode.EndPos.Line)},
127140
)
128141

129-
if nearest.StartPoint().Row != uint32(mainTraceNode.Line) {
142+
if nearest.StartPoint().Row != uint32(mainTraceNode.StartPos.Line) {
130143
cursor := sitter.NewTreeCursor(nearest)
131-
nearest = nearestNodeFromPos(cursor, mainTraceNode.Position)
144+
nearest = nearestNodeFromPos(cursor, mainTraceNode.StartPos)
132145
}
133146

134-
contextData.MainError = MainError{
147+
// further analyze main error
148+
contextData.MainError = &MainError{
135149
ErrorNode: &mainTraceNode,
136150
Document: doc,
137151
Nearest: WrapNode(doc, nearest),
138152
}
139153

154+
if contextData.MainError != nil && template.OnAnalyzeErrorFn != nil {
155+
template.OnAnalyzeErrorFn(contextData, contextData.MainError)
156+
}
157+
140158
return template, contextData, nil
141159
}
142160

143161
func (e *ErrgoEngine) Translate(template *CompiledErrorTemplate, contextData *ContextData) string {
162+
expGen := &ExplainGenerator{errorName: template.Name}
163+
fixGen := &BugFixGenerator{}
164+
144165
// execute error generator function
145-
explanation := template.OnGenExplainFn(contextData)
146-
// TODO: execute bug fix generator function
147-
_ = template.OnGenBugFixFn(contextData)
148-
return explanation
166+
template.OnGenExplainFn(contextData, expGen)
167+
template.OnGenBugFixFn(contextData, fixGen)
168+
169+
output := e.OutputGen.Generate(contextData, expGen, fixGen)
170+
defer e.OutputGen.Reset()
171+
return output
149172
}

error_template.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import (
66
"strings"
77
)
88

9+
type GenAnalyzeErrorFn func(cd *ContextData, m *MainError)
10+
911
type ErrorTemplate struct {
1012
Name string
1113
Pattern string
1214
StackTracePattern string
15+
OnAnalyzeErrorFn GenAnalyzeErrorFn
1316
OnGenExplainFn GenExplainFn
1417
OnGenBugFixFn GenBugFixFn
1518
}

error_template_test.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import (
99

1010
type TestAnalyzer struct{}
1111

12+
func (TestAnalyzer) FallbackSymbol() lib.Symbol {
13+
return nil
14+
}
15+
1216
func (TestAnalyzer) AnalyzeNode(lib.SyntaxNode) lib.Symbol {
1317
return nil
1418
}
@@ -24,16 +28,17 @@ var testLanguage = &lib.Language{
2428
LocationConverter: func(path, pos string) lib.Location {
2529
return lib.Location{
2630
DocumentPath: path,
27-
Position: lib.Position{0, 0, 0},
31+
StartPos: lib.Position{0, 0, 0},
32+
EndPos: lib.Position{0, 0, 0},
2833
}
2934
},
3035
AnalyzerFactory: func(cd *lib.ContextData) lib.LanguageAnalyzer {
3136
return TestAnalyzer{}
3237
},
3338
}
3439

35-
func emptyExplainFn(cd *lib.ContextData) string { return "" }
36-
func emptyBugFixFn(cd *lib.ContextData) []lib.BugFix { return make([]lib.BugFix, 0) }
40+
func emptyExplainFn(cd *lib.ContextData, gen *lib.ExplainGenerator) {}
41+
func emptyBugFixFn(cd *lib.ContextData, gen *lib.BugFixGenerator) {}
3742

3843
func setupTemplate(template lib.ErrorTemplate) (*lib.CompiledErrorTemplate, error) {
3944
errorTemplates := lib.ErrorTemplates{}
Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,108 @@
11
package java
22

3-
import lib "github.com/nedpals/errgoengine"
3+
import (
4+
"strings"
5+
6+
lib "github.com/nedpals/errgoengine"
7+
)
8+
9+
type arithExceptionKind int
10+
11+
const (
12+
unknown arithExceptionKind = 0
13+
dividedByZero arithExceptionKind = iota
14+
nonTerminatingDecimal arithExceptionKind = iota
15+
)
16+
17+
type arithExceptionCtx struct {
18+
kind arithExceptionKind
19+
}
420

521
var ArithmeticException = lib.ErrorTemplate{
622
Name: "ArithmeticException",
723
Pattern: runtimeErrorPattern("java.lang.ArithmeticException", "(?P<reason>.+)"),
8-
OnGenExplainFn: func(cd *lib.ContextData) string {
24+
OnAnalyzeErrorFn: func(cd *lib.ContextData, err *lib.MainError) {
25+
ctx := arithExceptionCtx{}
926
reason := cd.Variables["reason"]
27+
query := ""
28+
1029
switch reason {
1130
case "/ by zero":
12-
return "One of your variables initialized a double value by dividing a number to zero"
31+
ctx.kind = dividedByZero
32+
query = "(_) \"/\" ((decimal_integer_literal) @literal (#eq? @literal \"0\"))"
1333
case "Non-terminating decimal expansion; no exact representable decimal result.":
14-
return "TODO"
34+
ctx.kind = nonTerminatingDecimal
35+
query = "(method_invocation) @methodCall (#eq? @methodCall \".divide\")"
1536
default:
16-
return "Unknown ArithmeticException"
37+
ctx.kind = unknown
38+
}
39+
40+
if len(query) != 0 {
41+
lib.QueryNode(cd.MainError.Nearest, strings.NewReader(query), func(ctx lib.QueryNodeCtx) bool {
42+
match := ctx.Cursor.FilterPredicates(ctx.Match, []byte(cd.MainError.Nearest.Doc.Contents))
43+
for _, c := range match.Captures {
44+
node := lib.WrapNode(cd.MainError.Nearest.Doc, c.Node)
45+
err.Nearest = node
46+
return false
47+
}
48+
return true
49+
})
50+
}
51+
52+
err.Context = ctx
53+
},
54+
OnGenExplainFn: func(cd *lib.ContextData, gen *lib.ExplainGenerator) {
55+
ctx := cd.MainError.Context.(arithExceptionCtx)
56+
switch ctx.kind {
57+
case dividedByZero:
58+
gen.Add("This error is raised when you try to perform arithmetic operations that are not mathematically possible, such as division by zero.")
59+
case nonTerminatingDecimal:
60+
gen.Add("This error is raised when dividing two `BigDecimal` numbers, and the division operation results in a non-terminating decimal expansion, meaning the division produces a non-repeating and non-terminating decimal.")
61+
case unknown:
62+
gen.Add("You just encountered an unknown `ArithmeticException` error of which we cannot explain to you properly.")
1763
}
1864
},
19-
OnGenBugFixFn: func(cd *lib.ContextData) []lib.BugFix {
20-
// TODO:
21-
return make([]lib.BugFix, 0)
65+
OnGenBugFixFn: func(cd *lib.ContextData, gen *lib.BugFixGenerator) {
66+
ctx := cd.MainError.Context.(arithExceptionCtx)
67+
switch ctx.kind {
68+
case dividedByZero:
69+
gen.Add("Avoid dividing by zero.", func(s *lib.BugFixSuggestion) {
70+
s.AddStep("To fix the 'ArithmeticException: / by zero', you need to ensure you are not dividing by zero, which is mathematically undefined.").
71+
AddFix(lib.FixSuggestion{
72+
NewText: "1",
73+
Description: "This adjustment replaces the division by zero with a value that is not zero, ensuring the operation is valid. Division by zero is mathematically undefined, causing an 'ArithmeticException'. By changing the denominator to a non-zero value, you prevent the error.",
74+
StartPosition: cd.MainError.Nearest.StartPosition(),
75+
EndPosition: cd.MainError.Nearest.EndPosition(),
76+
})
77+
})
78+
case nonTerminatingDecimal:
79+
gen.Add("Ensure precise division", func(s *lib.BugFixSuggestion) {
80+
s.AddStep("To fix the 'ArithmeticException: Non-terminating decimal expansion', you need to ensure the division operation is precise.").
81+
AddFix(lib.FixSuggestion{
82+
NewText: ", RoundingMode.HALF_UP)",
83+
StartPosition: cd.MainError.Nearest.EndPosition(),
84+
EndPosition: cd.MainError.Nearest.EndPosition(),
85+
})
86+
})
87+
88+
if parent := cd.MainError.Nearest.Parent(); parent.Type() == "block" {
89+
gen.Add("Catch ArithmeticException", func(s *lib.BugFixSuggestion) {
90+
firstChild := parent.FirstNamedChild()
91+
lastChild := parent.LastNamedChild()
92+
93+
s.AddStep("Handle the ArithmeticException by wrapping the division operation in a try-catch block to manage the potential exception and inform the user about the non-terminating result.").
94+
AddFix(lib.FixSuggestion{
95+
NewText: "try {",
96+
StartPosition: firstChild.StartPosition(),
97+
EndPosition: firstChild.StartPosition(),
98+
}).
99+
AddFix(lib.FixSuggestion{
100+
NewText: "} catch (ArithmeticException e) {\n\tSystem.out.println(\"Non-terminating result: \" + e.getMessage());\n}",
101+
StartPosition: lastChild.StartPosition(),
102+
EndPosition: lastChild.StartPosition(),
103+
})
104+
})
105+
}
106+
}
22107
},
23108
}

0 commit comments

Comments
 (0)