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,15 +1,14 @@
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)
if (err != nil) { if (err != nil) {

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,14 +3,17 @@ package lexer
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/jorenchik/mdemory/src/compiler/comperror"
) )
var buffer []rune var buffer []rune
var row int32 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 (
@@ -91,7 +94,7 @@ func makeTokenWithTokenBuffer(
buffer = []rune{} buffer = []rune{}
} }
func TokenizeMdem(fileRunes []rune) ( []Token, error ) { func TokenizeMdem(fileRunes []rune) ([]Token, error) {
tokens = []Token{} tokens = []Token{}
buffer = []rune{} buffer = []rune{}
row = 1 row = 1
@@ -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]
@@ -131,43 +133,53 @@ func TokenizeMdem(fileRunes []rune) ( []Token, error ) {
switch c { switch c {
case '[': case '[':
makeTokenWithTokenBuffer(IdentifierStart, 1, TextFragment) makeTokenWithTokenBuffer(IdentifierStart, 1, TextFragment)
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
previousColumn = column previousColumn = column
textStarted = false textStarted = false
case '{': case '{':
makeTokenWithTokenBuffer(SectionStart, 1, Identifier) makeTokenWithTokenBuffer(SectionStart, 1, Identifier)
previousRow = row previousRow = row
previousColumn = column previousColumn = column
textStarted = false textStarted = false
case '}': case '}':
makeTokenWithTokenBuffer(SectionEnd, 1, TextFragment) makeTokenWithTokenBuffer(SectionEnd, 1, TextFragment)
previousRow = row previousRow = row
previousColumn = column previousColumn = column
textStarted = false textStarted = false
case '-': case '-':
makeTokenWithTokenBuffer(ElementDashStart, 1, TextFragment) makeTokenWithTokenBuffer(ElementDashStart, 1, TextFragment)
previousRow = row previousRow = row
previousColumn = column previousColumn = column
textStarted = false textStarted = false
case '>': case '>':
makeTokenWithTokenBuffer(QuestionEnd, 1, TextFragment) makeTokenWithTokenBuffer(QuestionEnd, 1, TextFragment)
previousRow = row previousRow = row
previousColumn = column previousColumn = column
case '+': case '+':
makeTokenWithTokenBuffer(ElementPlusStart, 1, TextFragment) makeTokenWithTokenBuffer(ElementPlusStart, 1, TextFragment)
previousRow = row previousRow = row
previousColumn = column previousColumn = column
textStarted = false textStarted = false
} }
column += 1 column += 1
} }
@@ -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"
) )
@@ -32,8 +33,8 @@ type MultipleChoiceQuestion struct {
func (question SingleAnswerQuestion) ToString() string { func (question SingleAnswerQuestion) ToString() string {
return fmt.Sprintf( return fmt.Sprintf(
"%20s: section: %-10s id: %-10s %-30s: %-30s", "%20s: section: %-10s id: %-10s %-30s: %-30s",
"<Single choice>", "<Single choice>",
question.Section, question.Section,
question.ID, question.ID,
strings.Trim(question.Question, "\t\n "), strings.Trim(question.Question, "\t\n "),
@@ -44,8 +45,8 @@ func (question SingleAnswerQuestion) ToString() string {
func (question MultipleChoiceQuestion) ToString() string { func (question MultipleChoiceQuestion) ToString() string {
acc := "" acc := ""
acc += fmt.Sprintf( acc += fmt.Sprintf(
"%20s: section: %-10s id: %-10s %-30s", "%20s: section: %-10s id: %-10s %-30s",
"<Multi choice>", "<Multi choice>",
question.Section, question.Section,
question.ID, question.ID,
question.Question, question.Question,
@@ -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 {
@@ -99,18 +90,18 @@ func parserAutomata() map[lexer.TokenType][]lexer.TokenType {
} }
automata[lexer.QuestionEnd] = []lexer.TokenType{ automata[lexer.QuestionEnd] = []lexer.TokenType{
lexer.ElementDashStart, lexer.ElementDashStart,
lexer.ElementPlusStart, lexer.ElementPlusStart,
} }
automata[lexer.ElementDashStart] = []lexer.TokenType{ automata[lexer.ElementDashStart] = []lexer.TokenType{
lexer.IdentifierStart, lexer.IdentifierStart,
lexer.TextFragment, lexer.TextFragment,
} }
automata[lexer.ElementPlusStart] = []lexer.TokenType{ automata[lexer.ElementPlusStart] = []lexer.TokenType{
lexer.TextFragment, lexer.TextFragment,
} }
automata[lexer.Identifier] = []lexer.TokenType{ automata[lexer.Identifier] = []lexer.TokenType{
lexer.IdentifierEnd, lexer.IdentifierEnd,
lexer.SectionStart, lexer.SectionStart,
} }
automata[lexer.IdentifierStart] = []lexer.TokenType{ automata[lexer.IdentifierStart] = []lexer.TokenType{
lexer.Identifier, lexer.Identifier,
@@ -123,37 +114,49 @@ func parserAutomata() map[lexer.TokenType][]lexer.TokenType {
} }
automata[lexer.SectionStart] = []lexer.TokenType{ automata[lexer.SectionStart] = []lexer.TokenType{
lexer.ElementDashStart, lexer.ElementDashStart,
lexer.SectionIdentifierStart, lexer.SectionIdentifierStart,
lexer.EOF, lexer.EOF,
} }
automata[lexer.SectionEnd] = []lexer.TokenType{ automata[lexer.SectionEnd] = []lexer.TokenType{
lexer.SectionIdentifierStart, lexer.SectionIdentifierStart,
lexer.ElementDashStart, lexer.ElementDashStart,
lexer.EOF, lexer.EOF,
} }
automata[lexer.SOF] = []lexer.TokenType{ automata[lexer.SOF] = []lexer.TokenType{
lexer.ElementDashStart, lexer.ElementDashStart,
lexer.SectionIdentifierStart, lexer.SectionIdentifierStart,
lexer.EOF, lexer.EOF,
} }
automata[lexer.EOF] = []lexer.TokenType{} automata[lexer.EOF] = []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,
} }
} }
} }
@@ -163,76 +166,76 @@ func ValidateGrammar(tokens []lexer.Token) error {
var DEBUG bool = true var DEBUG bool = true
func ParseQuestions(tokens []lexer.Token) ([]Question, error) { func ParseQuestions(tokens []lexer.Token) ([]Question, error) {
err := ValidateGrammar(tokens) err := ValidateGrammar(tokens)
if err != nil { if err != nil {
return nil, err return nil, err
} }
questions := []Question{} questions := []Question{}
section := "" section := ""
i := 0 i := 0
if DEBUG { if DEBUG {
fmt.Printf("Parser output:\n") fmt.Printf("Parser output:\n")
} }
for { for {
if i >= len(tokens) { if i >= len(tokens) {
break break
} }
// - [identifier] question_token > // - [identifier] question_token >
if tokens[i].TokenType == lexer.ElementDashStart { if tokens[i].TokenType == lexer.ElementDashStart {
var id string var id string
var question string var question string
var questionElements []QuestionElement var questionElements []QuestionElement
if tokens[i + 1].TokenType == lexer.IdentifierStart { if tokens[i+1].TokenType == lexer.IdentifierStart {
id = tokens[i + 2].Content id = tokens[i+2].Content
question = tokens[i + 4].Content question = tokens[i+4].Content
questionElements = []QuestionElement{} questionElements = []QuestionElement{}
i += 6 i += 6
} else { } else {
id = "" id = ""
question = tokens[i + 1].Content question = tokens[i+1].Content
questionElements = []QuestionElement{} questionElements = []QuestionElement{}
i += 3 i += 3
} }
for { for {
// Pointer is on the start of an element // Pointer is on the start of an element
// - a_question > // - a_question >
// - [identifier] a_question > // - [identifier] a_question >
// - an_element // - an_element
// terminate if we encounter a question. // terminate if we encounter a question.
if i + 3 < len(tokens) && if i+3 < len(tokens) &&
tokens[i + 3].TokenType != lexer.EOF { tokens[i+3].TokenType != lexer.EOF {
offset := 0 offset := 0
if tokens[i + 1].TokenType == lexer.IdentifierStart { if tokens[i+1].TokenType == lexer.IdentifierStart {
offset = 5 offset = 5
} else { } else {
offset = 2 offset = 2
} }
if i + offset < len(tokens) && if i+offset < len(tokens) &&
tokens[i + offset].TokenType == lexer.QuestionEnd { tokens[i+offset].TokenType == lexer.QuestionEnd {
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 {
questionElement.isDash = true questionElement.isDash = true
} else { } else {
questionElement.isDash = false questionElement.isDash = false
} }
questionElement.content = tokens[i + 1].Content questionElement.content = tokens[i+1].Content
questionElements = append(questionElements, questionElement) questionElements = append(questionElements, questionElement)
i += 2 i += 2
} }
@@ -253,9 +256,9 @@ func ParseQuestions(tokens []lexer.Token) ([]Question, error) {
} }
question.Choices = choices question.Choices = choices
questions = append(questions, question) questions = append(questions, question)
if DEBUG { if DEBUG {
fmt.Printf("%s", question.ToString()) fmt.Printf("%s", question.ToString())
} }
} else if len(questionElements) == 1 { } else if len(questionElements) == 1 {
question := SingleAnswerQuestion{ question := SingleAnswerQuestion{
ID: id, ID: id,
@@ -266,32 +269,32 @@ func ParseQuestions(tokens []lexer.Token) ([]Question, error) {
question.Section = section question.Section = section
} }
questions = append(questions, question) questions = append(questions, question)
if DEBUG { if DEBUG {
fmt.Printf("%s\n", question.ToString()) fmt.Printf("%s\n", question.ToString())
} }
} }
} else if tokens[i].TokenType == lexer.SectionIdentifierStart { } else if tokens[i].TokenType == lexer.SectionIdentifierStart {
section = tokens[i + 1].Content section = tokens[i+1].Content
i += 3 i += 3
if DEBUG { if DEBUG {
fmt.Printf("Started section: %s\n", section) fmt.Printf("Started section: %s\n", section)
} }
} else if tokens[i].TokenType == lexer.SectionEnd { } else if tokens[i].TokenType == lexer.SectionEnd {
section = "" section = ""
i += 1 i += 1
if DEBUG { if DEBUG {
fmt.Printf("Section ended: %s\n", section) fmt.Printf("Section ended: %s\n", section)
} }
} else if tokens[i].TokenType == lexer.EOF { } else if tokens[i].TokenType == lexer.EOF {
if DEBUG { if DEBUG {
fmt.Printf("File terminated: EOF\n") fmt.Printf("File terminated: EOF\n")
} }
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.