error handling and transformation improvements

Added vscode config for easy debug including all modules.
This commit is contained in:
jorenchik
2024-09-14 11:50:57 +03:00
parent a83d69aeb2
commit 9d6fa5b995
11 changed files with 11329 additions and 272 deletions

File diff suppressed because it is too large Load Diff

25
memorybase/faulty.mdem Normal file
View File

@@ -0,0 +1,25 @@
- Another question? >
- Answer
- Answer
- Answer
- Answer
- Answer
oejcoewjic
- Question? >
- Answer
fwefew
dwedew
dwe
dee

View File

@@ -1,110 +1,42 @@
- [cap_riga] 1 What is the capital of Latvia? >
- [cap_riga] What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] 2 What is the capital of Latvia? >
- [cap_riga] What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] 3 What is the capital of Latvia? >
- [cap_riga] What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 4 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 5 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 6 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 7 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 8 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 9 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 10 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 11 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 12 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 13 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 14 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 15 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 16 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 17 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 18 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 19 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 20 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? > - [cap_riga] 21 What is the capital of Latvia? >
- Riga - Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga
- [cap_riga] What is the capital of Latvia? >
- Riga

26
src/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,26 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}"
},
{
"name": "Debug with Build Flags",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/mdemory-app-qt/main.go",
// "buildFlags": "-gcflags='all=-N -l'",
"args": [],
"env": {},
"cwd": "${workspaceFolder}"
}
]
}

View File

@@ -1,14 +1,13 @@
package api package api
import ( import (
"github.com/jorenchik/mdemory/src/compiler/lexer" "github.com/jorenchik/mdemory/src/compiler/lexer"
"github.com/jorenchik/mdemory/src/compiler/parser" "github.com/jorenchik/mdemory/src/compiler/parser"
) )
func Compile(fileContents string) ([]parser.Question, error) { func Compile(fileContents string) ([]parser.Question, error) {
tokens, err := lexer.TokenizeMdem([]rune(fileContents)) tokens, err := lexer.TokenizeMdem([]rune(fileContents))
if err != nil { if (err != nil) {
return nil, err return nil, err
} }
questions, err := parser.ParseQuestions(tokens) questions, err := parser.ParseQuestions(tokens)

View File

@@ -0,0 +1,15 @@
package comperror
import (
"fmt"
)
type PositionErr struct {
Message string
Row int32
Column int32
}
func (e PositionErr) Error() string {
return fmt.Sprintf("%d:%d - %s", e.Row, e.Column, e.Message)
}

View File

@@ -3,6 +3,8 @@ package lexer
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/jorenchik/mdemory/src/compiler/comperror"
) )
var buffer []rune var buffer []rune
@@ -10,7 +12,8 @@ var row int32
var column int32 var column int32
var previousRow int32 var previousRow int32
var previousColumn int32 var previousColumn int32
var textStarted bool var textStarted bool = false
var identifierStarted bool = false
type TokenType int type TokenType int
const ( const (
@@ -104,7 +107,6 @@ func TokenizeMdem(fileRunes []rune) ( []Token, error ) {
return []Token{}, nil return []Token{}, nil
} }
for i := 0; i < len(fileRunes); i++ { for i := 0; i < len(fileRunes); i++ {
c := fileRunes[i] c := fileRunes[i]
@@ -134,11 +136,21 @@ func TokenizeMdem(fileRunes []rune) ( []Token, error ) {
previousRow = row previousRow = row
previousColumn = column previousColumn = column
textStarted = false textStarted = false
identifierStarted = true
case ']': case ']':
if !identifierStarted {
return []Token{}, comperror.PositionErr{
Message: "Cannot end identifier if it is not started",
Row: row,
Column: column,
}
}
makeTokenWithTokenBuffer(IdentifierEnd, 1, Identifier) makeTokenWithTokenBuffer(IdentifierEnd, 1, Identifier)
previousRow = row previousRow = row
previousColumn = column previousColumn = column
textStarted = false textStarted = false
identifierStarted = false
case '#': case '#':
makeTokenWithTokenBuffer(SectionIdentifierStart, 1, TextFragment) makeTokenWithTokenBuffer(SectionIdentifierStart, 1, TextFragment)
previousRow = row previousRow = row
@@ -187,17 +199,17 @@ func TokenizeMdem(fileRunes []rune) ( []Token, error ) {
func ToString (ttype *TokenType) string { func ToString (ttype *TokenType) string {
switch *ttype { switch *ttype {
case TextFragment: return "TextFragment" case TextFragment: return "text fragment"
case QuestionEnd: return "QuestionEnd" case QuestionEnd: return "question end symbol"
case ElementDashStart: return "ElementDashStart" case ElementDashStart: return "dash element start"
case ElementPlusStart: return "ElementPlusStart" case ElementPlusStart: return "plus element start"
case Identifier: return "Identifier" case Identifier: return "identifier"
case IdentifierStart: return "IdentifierStart" case IdentifierStart: return "start of identifier"
case IdentifierEnd: return "IdentifierEnd" case IdentifierEnd: return "end of identifier"
case SectionIdentifierStart: return "SectionIdentifierStart" case SectionIdentifierStart: return "identifier"
case SectionStart: return "SectionStart" case SectionStart: return "start of a section"
case SectionEnd: return "SectionEnd" case SectionEnd: return "end of a section"
case EOF: return "EndOfFile" case EOF: return "end of a file"
default: return "NOT_RECOGNIZED" default: return "unrecognized token"
} }
} }

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/jorenchik/mdemory/src/compiler/comperror"
"github.com/jorenchik/mdemory/src/compiler/lexer" "github.com/jorenchik/mdemory/src/compiler/lexer"
) )
@@ -67,16 +68,6 @@ type QuestionElement struct {
var automata map[lexer.TokenType][]lexer.TokenType var automata map[lexer.TokenType][]lexer.TokenType
type CompilerErr struct {
message string
row int32
column int32
}
func (e CompilerErr) Error() string {
return fmt.Sprintf("%d:%d - %s", e.row, e.column, e.message)
}
func contains(s []lexer.TokenType, e lexer.TokenType) bool { func contains(s []lexer.TokenType, e lexer.TokenType) bool {
for _, a := range s { for _, a := range s {
if a == e { if a == e {
@@ -140,20 +131,32 @@ func parserAutomata() map[lexer.TokenType][]lexer.TokenType {
return automata return automata
} }
func capitalize(str string) string {
if len(str) <= 0 {
return str
}
firstCapital := strings.ToUpper(str[0:1])
if len(str) == 1 {
return firstCapital
} else {
return firstCapital + str[1:]
}
}
func ValidateGrammar(tokens []lexer.Token) error { func ValidateGrammar(tokens []lexer.Token) error {
automata = parserAutomata() automata = parserAutomata()
for i := 0; i < len(tokens)-1; i++ { for i := 0; i < len(tokens)-1; i++ {
token := tokens[i] token := tokens[i]
nextToken := tokens[i+1] nextToken := tokens[i+1]
if !contains(automata[token.TokenType], nextToken.TokenType) { if !contains(automata[token.TokenType], nextToken.TokenType) {
return CompilerErr{ return comperror.PositionErr{
message: fmt.Sprintf( Message: fmt.Sprintf(
"Token %s cannot precede %s\n", "%s cannot precede %s",
lexer.ToString(&token.TokenType), capitalize(lexer.ToString(&token.TokenType)),
lexer.ToString(&nextToken.TokenType), lexer.ToString(&nextToken.TokenType),
), ),
row: token.Row, Row: token.Row,
column: token.Column, Column: token.Column,
} }
} }
} }
@@ -216,15 +219,15 @@ func ParseQuestions(tokens []lexer.Token) ([]Question, error) {
break break
} }
if offset == 5 && tokens[i+5].TokenType != lexer.QuestionEnd { if offset == 5 && tokens[i+5].TokenType != lexer.QuestionEnd {
return nil, CompilerErr{ return nil, comperror.PositionErr{
message: "Cannot have an identifier here", Message: "Cannot have an identifier here",
row: tokens[i].Row, Row: tokens[i].Row,
column: tokens[i].Column, Column: tokens[i].Column,
} }
} }
} }
if (i + 2 >= len(tokens)) { if i+2 >= len(tokens) {
break; break
} }
questionElement := QuestionElement{} questionElement := QuestionElement{}
if tokens[i].TokenType == lexer.ElementDashStart { if tokens[i].TokenType == lexer.ElementDashStart {
@@ -288,10 +291,10 @@ func ParseQuestions(tokens []lexer.Token) ([]Question, error) {
} }
break break
} else { } else {
return nil, CompilerErr{ return nil, comperror.PositionErr{
message: "Unexpeced token: %s", Message: "Unexpeced token",
row: tokens[i].Row, Row: tokens[i].Row,
column: tokens[i].Column, Column: tokens[i].Column,
} }
} }
} }

View File

@@ -1,23 +1,16 @@
package main package main
// iter: 1
// [1] 2 3 4 5 6 7
// 2
// press 4
// 1 2 3 [4] 5 6 7
// 4 (page num) - 2
import ( import (
"fmt" "fmt"
"math/rand"
"os" "os"
"regexp"
"strconv"
"strings" "strings"
"time" "time"
"strconv"
"math/rand"
"github.com/jorenchik/mdemory/src/compiler/api" "github.com/jorenchik/mdemory/src/compiler/api"
"github.com/jorenchik/mdemory/src/compiler/comperror"
"github.com/jorenchik/mdemory/src/compiler/parser" "github.com/jorenchik/mdemory/src/compiler/parser"
"github.com/therecipe/qt/core" "github.com/therecipe/qt/core"
"github.com/therecipe/qt/widgets" "github.com/therecipe/qt/widgets"
@@ -42,6 +35,12 @@ var questions [] parser.Question
var paginationButtons []*widgets.QToolButton var paginationButtons []*widgets.QToolButton
var paginationLabel *widgets.QLabel var paginationLabel *widgets.QLabel
var errorBox *widgets.QWidget
var errorLabel *widgets.QLabel
var multispacePattern *regexp.Regexp = regexp.MustCompile(`\s\s+`)
// var currentPage = 0 // var currentPage = 0
var PER_PAGE = 10 var PER_PAGE = 10
var pages []Page var pages []Page
@@ -156,13 +155,7 @@ func CreateMdem() *Mdem {
} }
func CreateMdems(questions *[]parser.Question) { func CreateMdems(questions *[]parser.Question) {
if spacerInitialized {
hMdemScroll.RemoveItem(mdemSpacer) hMdemScroll.RemoveItem(mdemSpacer)
} else {
mdemSpacer = widgets.NewQSpacerItem(
0, 0, widgets.QSizePolicy__Maximum, widgets.QSizePolicy__Minimum,
)
}
for i := range mdems { for i := range mdems {
if mdems[i].wMdem.IsVisible() { if mdems[i].wMdem.IsVisible() {
@@ -179,6 +172,12 @@ func CreateMdems(questions *[]parser.Question) {
} }
// destroy widgets // destroy widgets
transformAnswer := func(answer string) string {
answer = strings.ReplaceAll(answer, "\t", " ")
answer = strings.ReplaceAll(answer, "\n", " ")
answer = multispacePattern.ReplaceAllString(answer, ` `)
return answer
}
for i := range *questions { for i := range *questions {
question := (*questions)[i] question := (*questions)[i]
switch question.(type) { switch question.(type) {
@@ -186,10 +185,10 @@ func CreateMdems(questions *[]parser.Question) {
mdems[i].wFrontText.SetText( mdems[i].wFrontText.SetText(
question.(parser.SingleAnswerQuestion).Question, question.(parser.SingleAnswerQuestion).Question,
) )
answer := strings.Trim( answer := question.(parser.SingleAnswerQuestion).Answer
question.(parser.SingleAnswerQuestion).Answer, " \t\n", answer = transformAnswer(answer)
) answer = fmt.Sprintf("- %s", answer)
mdems[i].backLabels[0].SetText(fmt.Sprintf("- %s", answer)) mdems[i].backLabels[0].SetText(answer)
if mdems[i].wBack.IsVisible() { if mdems[i].wBack.IsVisible() {
mdems[i].wBack.Hide() mdems[i].wBack.Hide()
} }
@@ -200,11 +199,13 @@ func CreateMdems(questions *[]parser.Question) {
question.(parser.MultipleChoiceQuestion).Question, question.(parser.MultipleChoiceQuestion).Question,
) )
for k := range choices { for k := range choices {
answer := strings.Trim(choices[k].Answer, " \t\n") answer := choices[k].Answer
answer = transformAnswer(answer)
answer = fmt.Sprintf("- %s", answer)
if k < len(mdems[i].backLabels) { if k < len(mdems[i].backLabels) {
mdems[i].backLabels[k].SetText(answer) mdems[i].backLabels[k].SetText(answer)
} else { } else {
label := widgets.NewQLabel2(fmt.Sprintf("- %s", answer), nil, 0) label := widgets.NewQLabel2(answer, nil, 0)
mdems[i].backLabels = append( mdems[i].backLabels = append(
mdems[i].backLabels, mdems[i].backLabels,
label, label,
@@ -219,15 +220,6 @@ func CreateMdems(questions *[]parser.Question) {
} }
} }
if !spacerInitialized {
mdemSpacer = widgets.NewQSpacerItem(
40,
40,
widgets.QSizePolicy__Minimum,
widgets.QSizePolicy__Expanding,
)
spacerInitialized = true
}
hMdemScroll.AddItem(mdemSpacer) hMdemScroll.AddItem(mdemSpacer)
} }
@@ -313,6 +305,9 @@ func OpenModelFile(
if model.IsDir(index) { if model.IsDir(index) {
return return
} }
if errorBox.IsVisible() {
errorBox.Hide()
}
filePath := model.FilePath(index) filePath := model.FilePath(index)
fileContents, err := os.ReadFile(filePath) fileContents, err := os.ReadFile(filePath)
if err != nil { if err != nil {
@@ -329,13 +324,25 @@ func OpenModelFile(
start := time.Now().UnixMicro() start := time.Now().UnixMicro()
questions, err = api.Compile(string(fileContents)) questions, err = api.Compile(string(fileContents))
if err != nil { if err != nil {
widgets.QMessageBox_Critical( var errText string
nil, switch err.(type) {
"Compilation error", case comperror.PositionErr:
err.Error(), errText = fmt.Sprintf(
widgets.QMessageBox__Ok, "%s on line %d, column %d",
widgets.QMessageBox__Ok, err.(comperror.PositionErr).Message,
err.(comperror.PositionErr).Row,
err.(comperror.PositionErr).Column,
) )
default:
errText = err.Error()
}
errorLabel.SetText(errText)
errorBox.Show()
for i := range(mdems) {
if mdems[i].wMdem.IsVisible() {
mdems[i].wMdem.Hide()
}
}
return return
} }
duration := float32(time.Now().UnixMicro()-start) / 1000 duration := float32(time.Now().UnixMicro()-start) / 1000
@@ -420,6 +427,24 @@ func main() {
mdemScroll.SetWidgetResizable(true) mdemScroll.SetWidgetResizable(true)
mdemContainer.SetLayout(hMdemScroll) mdemContainer.SetLayout(hMdemScroll)
errorBox = widgets.NewQWidget(nil, 0)
hErrorBox := widgets.NewQHBoxLayout2(nil)
errorBox.SetLayout(hErrorBox)
errorLabel = widgets.NewQLabel(nil, 0)
errorLabel.SetText("Error")
errorBox.Layout().AddWidget(errorLabel)
hErrorBox.AddStretch(1)
hMdemScroll.AddWidget(errorBox, 0, 0)
errorBox.SetObjectName("error")
errorBox.SetStyleSheet(`
QWidget#error {
border: 1px solid #dc143c;
background-color: white;
padding: 10px 0px;
}
`)
errorBox.Hide()
for i := 0; i < 40; i++ { for i := 0; i < 40; i++ {
mdem := CreateMdem() mdem := CreateMdem()
mdems = append( mdems = append(
@@ -428,6 +453,13 @@ func main() {
) )
hMdemScroll.AddWidget(mdem.wMdem, 0, 0) hMdemScroll.AddWidget(mdem.wMdem, 0, 0)
} }
mdemSpacer = widgets.NewQSpacerItem(
40,
40,
widgets.QSizePolicy__Minimum,
widgets.QSizePolicy__Expanding,
)
hMdemScroll.AddItem(mdemSpacer)
// CreateMdems // CreateMdems
rightLayout.AddWidget(mdemScroll, 1, 0) rightLayout.AddWidget(mdemScroll, 1, 0)

Binary file not shown.

Binary file not shown.