add c++ project and translate from go partially

This commit is contained in:
jorenchik
2024-09-15 20:02:19 +03:00
parent 9d6fa5b995
commit cd538159cf
49 changed files with 1552 additions and 0 deletions

1
src/go/compiler/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
compiler

View File

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

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

@@ -0,0 +1,30 @@
package main
import (
"fmt"
"os"
"github.com/jorenchik/mdemory/src/compiler/api"
)
func main() {
file, err := os.ReadFile(
"/home/jorenchik/Code/mdemory/src/compiler/input.mdem",
)
if (err != nil) {
fmt.Printf(
"Cannot open the input file: %s\n",
err.Error(),
)
return
}
fileContents := string(file)
fmt.Println("Compilation started...")
_, err = api.Compile(fileContents)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println("Compilation completed")
}

15
src/go/compiler/deck.moml Normal file
View File

@@ -0,0 +1,15 @@
[MathTest]
items = {graph_theory.networks, linear_algebra.*}
base_interval = 2
[Projects]
items = {}
intervals = {2, 6, 12, 14}
[Programming]
items = {programming.*}
[OldProgramming]
items = {old_programming.*}
disabled = false

3
src/go/compiler/go.mod Normal file
View File

@@ -0,0 +1,3 @@
module github.com/jorenchik/mdemory/src/compiler
go 1.22.5

0
src/go/compiler/go.sum Normal file
View File

View File

@@ -0,0 +1,215 @@
package lexer
import (
"fmt"
"strings"
"github.com/jorenchik/mdemory/src/compiler/comperror"
)
var buffer []rune
var row int32
var column int32
var previousRow int32
var previousColumn int32
var textStarted bool = false
var identifierStarted bool = false
type TokenType int
const (
TextFragment TokenType = iota
QuestionEnd
ElementDashStart
ElementPlusStart
Identifier
IdentifierStart
IdentifierEnd
SectionIdentifierStart
SectionStart
SectionEnd
SOF
EOF
)
type Token struct {
TokenType TokenType;
Content string;
Row int32;
Column int32;
}
func (token Token) ToString() string {
content := token.Content
if (token.TokenType == TextFragment) {
content = strings.Replace(
strings.Trim(content, " "),
"\n",
"\\n",
-1,
)
}
return fmt.Sprintf(
"%20s: %35s %3d:%3d\n",
ToString(&token.TokenType),
content,
token.Row,
token.Column,
)
}
var tokens []Token
func makeTokenWithTokenBuffer(
ttype TokenType,
tokenLen int32,
textType TokenType,
) {
if (len(strings.Trim(string(buffer), " \n\t")) - 1 > 0) {
textFragment := []rune{}
for i := 0; i < len(buffer) - int(tokenLen); i++ {
element := buffer[i]
textFragment = append(textFragment, element)
}
tokens = append(
tokens,
Token{
TokenType: textType,
Content: string(textFragment),
Row: int32(previousRow),
Column: int32(previousColumn),
},
)
}
tokens = append(
tokens,
Token{
TokenType: ttype,
Content: string(buffer[len(buffer)-int(tokenLen):]),
Row: int32(row),
Column: int32(column),
},
)
previousRow = row
previousColumn = column
buffer = []rune{}
}
func TokenizeMdem(fileRunes []rune) ([]Token, error) {
tokens = []Token{}
buffer = []rune{}
row = 1
column = 1
previousRow = -1
previousColumn = -1
textStarted = false
if (len(strings.Trim(string(fileRunes), " \n\t")) <= 0) {
return []Token{}, nil
}
for i := 0; i < len(fileRunes); i++ {
c := fileRunes[i]
// AdvancePointer
if (c == '\n') {
row += 1
column = 1
}
buffer = append(buffer, c)
// SkipWhitetext
if !textStarted {
if c == '\n' {
previousRow += 1
previousColumn = 1
} else if (c == ' ') {
previousColumn += 1
} else {
textStarted = true
}
}
// EmitTokens
switch c {
case '[':
makeTokenWithTokenBuffer(IdentifierStart, 1, TextFragment)
previousRow = row
previousColumn = column
textStarted = false
identifierStarted = true
case ']':
if !identifierStarted {
return []Token{}, comperror.PositionErr{
Message: "Cannot end identifier if it is not started",
Row: row,
Column: column,
}
}
makeTokenWithTokenBuffer(IdentifierEnd, 1, Identifier)
previousRow = row
previousColumn = column
textStarted = false
identifierStarted = false
case '#':
makeTokenWithTokenBuffer(SectionIdentifierStart, 1, TextFragment)
previousRow = row
previousColumn = column
textStarted = false
case '{':
makeTokenWithTokenBuffer(SectionStart, 1, Identifier)
previousRow = row
previousColumn = column
textStarted = false
case '}':
makeTokenWithTokenBuffer(SectionEnd, 1, TextFragment)
previousRow = row
previousColumn = column
textStarted = false
case '-':
makeTokenWithTokenBuffer(ElementDashStart, 1, TextFragment)
previousRow = row
previousColumn = column
textStarted = false
case '>':
makeTokenWithTokenBuffer(QuestionEnd, 1, TextFragment)
previousRow = row
previousColumn = column
case '+':
makeTokenWithTokenBuffer(ElementPlusStart, 1, TextFragment)
previousRow = row
previousColumn = column
textStarted = false
}
column += 1
}
// EmitEOF
makeTokenWithTokenBuffer(EOF, 0, TextFragment)
if true {
fmt.Printf("Lexer output:\n")
for _, el := range tokens {
fmt.Print(el.ToString())
}
fmt.Printf("End Lexer output\n")
}
return tokens, nil
}
func ToString (ttype *TokenType) string {
switch *ttype {
case TextFragment: return "text fragment"
case QuestionEnd: return "question end symbol"
case ElementDashStart: return "dash element start"
case ElementPlusStart: return "plus element start"
case Identifier: return "identifier"
case IdentifierStart: return "start of identifier"
case IdentifierEnd: return "end of identifier"
case SectionIdentifierStart: return "identifier"
case SectionStart: return "start of a section"
case SectionEnd: return "end of a section"
case EOF: return "end of a file"
default: return "unrecognized token"
}
}

View File

@@ -0,0 +1,302 @@
package parser
import (
"fmt"
"strings"
"github.com/jorenchik/mdemory/src/compiler/comperror"
"github.com/jorenchik/mdemory/src/compiler/lexer"
)
type Question interface {
ToString() string
}
type SingleAnswerQuestion struct {
ID string
Question string
Answer string
Section string
}
type Choice struct {
Answer string
IsCorrect bool
}
type MultipleChoiceQuestion struct {
ID string
Question string
Choices []Choice
Section string
}
func (question SingleAnswerQuestion) ToString() string {
return fmt.Sprintf(
"%20s: section: %-10s id: %-10s %-30s: %-30s",
"<Single choice>",
question.Section,
question.ID,
strings.Trim(question.Question, "\t\n "),
strings.Trim(question.Answer, "\t\n "),
)
}
func (question MultipleChoiceQuestion) ToString() string {
acc := ""
acc += fmt.Sprintf(
"%20s: section: %-10s id: %-10s %-30s",
"<Multi choice>",
question.Section,
question.ID,
question.Question,
)
for _, el := range question.Choices {
opener := '-'
if el.IsCorrect {
opener = '+'
}
acc += fmt.Sprintf("\t%c %s\n", opener, strings.Trim(el.Answer, "\t\n "))
}
return acc
}
type QuestionElement struct {
isDash bool
content string
}
var automata map[lexer.TokenType][]lexer.TokenType
func contains(s []lexer.TokenType, e lexer.TokenType) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
func parserAutomata() map[lexer.TokenType][]lexer.TokenType {
automata := make(map[lexer.TokenType][]lexer.TokenType)
automata[lexer.TextFragment] = []lexer.TokenType{
lexer.QuestionEnd,
lexer.ElementDashStart,
lexer.ElementPlusStart,
lexer.SectionIdentifierStart,
lexer.SectionStart,
lexer.EOF,
lexer.SectionEnd,
}
automata[lexer.QuestionEnd] = []lexer.TokenType{
lexer.ElementDashStart,
lexer.ElementPlusStart,
}
automata[lexer.ElementDashStart] = []lexer.TokenType{
lexer.IdentifierStart,
lexer.TextFragment,
}
automata[lexer.ElementPlusStart] = []lexer.TokenType{
lexer.TextFragment,
}
automata[lexer.Identifier] = []lexer.TokenType{
lexer.IdentifierEnd,
lexer.SectionStart,
}
automata[lexer.IdentifierStart] = []lexer.TokenType{
lexer.Identifier,
}
automata[lexer.IdentifierEnd] = []lexer.TokenType{
lexer.TextFragment,
}
automata[lexer.SectionIdentifierStart] = []lexer.TokenType{
lexer.Identifier,
}
automata[lexer.SectionStart] = []lexer.TokenType{
lexer.ElementDashStart,
lexer.SectionIdentifierStart,
lexer.EOF,
}
automata[lexer.SectionEnd] = []lexer.TokenType{
lexer.SectionIdentifierStart,
lexer.ElementDashStart,
lexer.EOF,
}
automata[lexer.SOF] = []lexer.TokenType{
lexer.ElementDashStart,
lexer.SectionIdentifierStart,
lexer.EOF,
}
automata[lexer.EOF] = []lexer.TokenType{}
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 {
automata = parserAutomata()
for i := 0; i < len(tokens)-1; i++ {
token := tokens[i]
nextToken := tokens[i+1]
if !contains(automata[token.TokenType], nextToken.TokenType) {
return comperror.PositionErr{
Message: fmt.Sprintf(
"%s cannot precede %s",
capitalize(lexer.ToString(&token.TokenType)),
lexer.ToString(&nextToken.TokenType),
),
Row: token.Row,
Column: token.Column,
}
}
}
return nil
}
var DEBUG bool = true
func ParseQuestions(tokens []lexer.Token) ([]Question, error) {
err := ValidateGrammar(tokens)
if err != nil {
return nil, err
}
questions := []Question{}
section := ""
i := 0
if DEBUG {
fmt.Printf("Parser output:\n")
}
for {
if i >= len(tokens) {
break
}
// - [identifier] question_token >
if tokens[i].TokenType == lexer.ElementDashStart {
var id string
var question string
var questionElements []QuestionElement
if tokens[i+1].TokenType == lexer.IdentifierStart {
id = tokens[i+2].Content
question = tokens[i+4].Content
questionElements = []QuestionElement{}
i += 6
} else {
id = ""
question = tokens[i+1].Content
questionElements = []QuestionElement{}
i += 3
}
for {
// Pointer is on the start of an element
// - a_question >
// - [identifier] a_question >
// - an_element
// terminate if we encounter a question.
if i+3 < len(tokens) &&
tokens[i+3].TokenType != lexer.EOF {
offset := 0
if tokens[i+1].TokenType == lexer.IdentifierStart {
offset = 5
} else {
offset = 2
}
if i+offset < len(tokens) &&
tokens[i+offset].TokenType == lexer.QuestionEnd {
break
}
if offset == 5 && tokens[i+5].TokenType != lexer.QuestionEnd {
return nil, comperror.PositionErr{
Message: "Cannot have an identifier here",
Row: tokens[i].Row,
Column: tokens[i].Column,
}
}
}
if i+2 >= len(tokens) {
break
}
questionElement := QuestionElement{}
if tokens[i].TokenType == lexer.ElementDashStart {
questionElement.isDash = true
} else {
questionElement.isDash = false
}
questionElement.content = tokens[i+1].Content
questionElements = append(questionElements, questionElement)
i += 2
}
if len(questionElements) > 1 {
question := MultipleChoiceQuestion{
ID: id,
Question: question,
}
choices := []Choice{}
for k := 0; k < len(questionElements); k++ {
choice := Choice{}
choice.Answer = questionElements[k].content
choice.IsCorrect = !questionElements[k].isDash
choices = append(choices, choice)
}
if section != "" {
question.Section = section
}
question.Choices = choices
questions = append(questions, question)
if DEBUG {
fmt.Printf("%s", question.ToString())
}
} else if len(questionElements) == 1 {
question := SingleAnswerQuestion{
ID: id,
Question: question,
Answer: questionElements[0].content,
}
if section != "" {
question.Section = section
}
questions = append(questions, question)
if DEBUG {
fmt.Printf("%s\n", question.ToString())
}
}
} else if tokens[i].TokenType == lexer.SectionIdentifierStart {
section = tokens[i+1].Content
i += 3
if DEBUG {
fmt.Printf("Started section: %s\n", section)
}
} else if tokens[i].TokenType == lexer.SectionEnd {
section = ""
i += 1
if DEBUG {
fmt.Printf("Section ended: %s\n", section)
}
} else if tokens[i].TokenType == lexer.EOF {
if DEBUG {
fmt.Printf("File terminated: EOF\n")
}
break
} else {
return nil, comperror.PositionErr{
Message: "Unexpeced token",
Row: tokens[i].Row,
Column: tokens[i].Column,
}
}
}
return questions, nil
}

2
src/go/mdemory-app-qt/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
build/
qtbox

View File

@@ -0,0 +1,23 @@
{
// 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 Go Program",
"type": "go",
"request": "launch",
"mode": "exec",
"program": "${workspaceFolder}/mdemory_debug",
"args": []
}
]
}

View File

@@ -0,0 +1,12 @@
module github.com/jorenchik/mdemory/src/mdemory-app-qt
go 1.23.0
require (
github.com/jorenchik/mdemory/src/compiler v0.0.0-00010101000000-000000000000
github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d
)
require github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e // indirect
replace github.com/jorenchik/mdemory/src/compiler => ../compiler

View File

@@ -0,0 +1,25 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e h1:XWcjeEtTFTOVA9Fs1w7n2XBftk5ib4oZrhzWk0B+3eA=
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d h1:T+d8FnaLSvM/1BdlDXhW4d5dr2F07bAbB+LpgzMxx+o=
github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

BIN
src/go/mdemory-app-qt/main Executable file

Binary file not shown.

View File

@@ -0,0 +1,545 @@
package main
import (
"fmt"
"math/rand"
"os"
"regexp"
"strconv"
"strings"
"time"
"github.com/jorenchik/mdemory/src/compiler/api"
"github.com/jorenchik/mdemory/src/compiler/comperror"
"github.com/jorenchik/mdemory/src/compiler/parser"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/widgets"
)
type Page struct {
start int;
end int;
}
type PaginationButton struct {
button *widgets.QToolButton;
pageIdx int;
}
var deckListLabel *widgets.QLabel
var hMdemScroll *widgets.QVBoxLayout
var mdemSpacer *widgets.QSpacerItem
var spacerInitialized bool = false
var workingPath string = "/home/jorenchik/Code/mdemory/memorybase"
var questions [] parser.Question
var paginationButtons []*widgets.QToolButton
var paginationLabel *widgets.QLabel
var errorBox *widgets.QWidget
var errorLabel *widgets.QLabel
var multispacePattern *regexp.Regexp = regexp.MustCompile(`\s\s+`)
// var currentPage = 0
var PER_PAGE = 10
var pages []Page
var currentPage = -1
var prevButton *widgets.QToolButton
var firstButton *widgets.QToolButton
var lastButton *widgets.QToolButton
var nextButton *widgets.QToolButton
type Mdem struct {
wMdem *widgets.QWidget;
wFrontText *widgets.QLabel;
wBack *widgets.QWidget;
backLabels []*widgets.QLabel;
showButton *widgets.QToolButton;
labelCount int;
}
var mdems []*Mdem
func (mdem *Mdem) showBacklabels() {
for i := range(len(mdem.backLabels)) {
if i < mdem.labelCount {
if !mdem.backLabels[i].IsVisible() {
mdem.backLabels[i].Show()
}
} else {
if mdem.backLabels[i].IsVisible() {
mdem.backLabels[i].Hide()
}
}
}
}
func CreateMdem() *Mdem {
// DefineMdem
wMdem := widgets.NewQWidget(nil, 0)
vMdem := widgets.NewQVBoxLayout()
wMdem.SetLayout(vMdem)
id := fmt.Sprintf("mdem_%d", 1)
wMdem.SetObjectName(id)
// InitFront
wFront := widgets.NewQWidget(nil, 0)
hFront := widgets.NewQHBoxLayout()
wFront.SetMinimumHeight(60)
wFront.SetLayout(hFront)
wFront.SetProperty("first", core.NewQVariant1(true))
wMdem.SetStyleSheet(fmt.Sprintf(`
QWidget#%s > QWidget {
border-right: 1px solid gray;
border-bottom: 1px solid gray;
border-left: 1px solid gray;
}
QWidget#%s > QWidget[first="true"] {
border-top: 1px solid gray;
}
`, id, id))
// AddFrontContent
elFrontText := widgets.NewQLabel(nil, 0)
hFront.AddWidget(elFrontText, 0, 0)
hFront.AddStretch(1)
showAnswer := widgets.NewQToolButton(nil)
showAnswer.SetText("Show")
hFront.AddWidget(showAnswer, 0, 0)
wBack := widgets.NewQWidget(nil, 0)
hBack := widgets.NewQVBoxLayout()
wBack.SetLayout(hBack)
vMdem.AddWidget(wBack, 0, 0)
// AddBackContent
var backLabels []*widgets.QLabel
for range(20) {
elBackText := widgets.NewQLabel(nil, 0)
hBack.AddWidget(elBackText, 0, 0)
backLabels = append(backLabels, elBackText)
}
vMdem.AddWidget(wFront, 0, 0)
vMdem.AddWidget(wBack, 0, 0)
vMdem.SetContentsMargins(0, 0, 0, 0)
vMdem.SetSpacing(0)
wBack.Hide()
wMdem.Hide()
mdem := Mdem{
wMdem,
elFrontText,
wBack,
backLabels,
showAnswer,
0,
}
mdem.showButton.ConnectClicked(func(checked bool) {
if mdem.wBack.IsVisible() {
mdem.wBack.Hide()
mdem.showButton.SetText("Show")
} else {
mdem.wBack.Show()
mdem.showButton.SetText("Hide")
}
mdem.showBacklabels()
})
return &mdem
}
func CreateMdems(questions *[]parser.Question) {
hMdemScroll.RemoveItem(mdemSpacer)
for i := range mdems {
if mdems[i].wMdem.IsVisible() {
mdems[i].wMdem.Hide()
}
}
if len(*questions) > len(mdems) {
for range(len(*questions) - len(mdems)) {
mdem := CreateMdem()
mdems = append(mdems, mdem)
hMdemScroll.AddWidget(mdem.wMdem, 0, 0)
}
}
// 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 {
question := (*questions)[i]
switch question.(type) {
case parser.SingleAnswerQuestion:
mdems[i].wFrontText.SetText(
question.(parser.SingleAnswerQuestion).Question,
)
answer := question.(parser.SingleAnswerQuestion).Answer
answer = transformAnswer(answer)
answer = fmt.Sprintf("- %s", answer)
mdems[i].backLabels[0].SetText(answer)
if mdems[i].wBack.IsVisible() {
mdems[i].wBack.Hide()
}
mdems[i].labelCount = 1
case parser.MultipleChoiceQuestion:
choices := question.(parser.MultipleChoiceQuestion).Choices
mdems[i].wFrontText.SetText(
question.(parser.MultipleChoiceQuestion).Question,
)
for k := range choices {
answer := choices[k].Answer
answer = transformAnswer(answer)
answer = fmt.Sprintf("- %s", answer)
if k < len(mdems[i].backLabels) {
mdems[i].backLabels[k].SetText(answer)
} else {
label := widgets.NewQLabel2(answer, nil, 0)
mdems[i].backLabels = append(
mdems[i].backLabels,
label,
)
mdems[i].wBack.Layout().AddWidget(label)
}
}
mdems[i].labelCount = len(choices)
}
if !mdems[i].wMdem.IsVisible() {
mdems[i].wMdem.Show()
}
}
hMdemScroll.AddItem(mdemSpacer)
}
func MakePages() {
pages = nil
for i := 0; i < (len(questions) / PER_PAGE) + 1; i++ {
startingIndex := PER_PAGE * i
amount := PER_PAGE
if (i == len(questions) / PER_PAGE) {
amount = len(questions) % PER_PAGE
}
pages = append(
pages,
Page{startingIndex, startingIndex + amount},
)
}
}
var PAGINATION_COUNT int = 8
var DISTANCE int = PAGINATION_COUNT / 2 - 1
func SwitchPage(pageIdx int) {
currentPage = pageIdx
for i := range(paginationButtons) {
paginationButtons[i].Hide()
}
l := 0
paginationLabel.SetText(fmt.Sprintf("Page: %d", pageIdx + 1))
for i := range(mdems) {
if mdems[i].wBack.IsVisible() {
mdems[i].wBack.Hide()
mdems[i].showButton.SetText("Show")
}
}
for k := -DISTANCE; k <= DISTANCE; k = k + 1 {
if pageIdx + k >= 0 && pageIdx + k < len(pages) {
button := paginationButtons[l]
button.SetText(fmt.Sprintf("%d", pageIdx + k + 1))
if pageIdx + k != pageIdx {
button.Show()
} else {
button.Hide()
}
l++
}
}
if pageIdx > 0 && len(pages) > 1 {
firstButton.Show()
} else {
firstButton.Hide()
}
if pageIdx < len(pages) - 1 && len(pages) > 1 {
lastButton.Show()
} else {
lastButton.Hide()
}
if len(pages) > 0 &&
currentPage < len(pages) - 1 {
nextButton.Show()
} else {
nextButton.Hide()
}
if len(pages) > 0 &&
currentPage >= 1 {
prevButton.Show()
} else {
prevButton.Hide()
}
page := pages[pageIdx]
pageSlice := questions[page.start:page.end]
CreateMdems(&pageSlice)
}
func OpenModelFile(
model *widgets.QFileSystemModel,
index *core.QModelIndex,
) {
if model.IsDir(index) {
return
}
if errorBox.IsVisible() {
errorBox.Hide()
}
filePath := model.FilePath(index)
fileContents, err := os.ReadFile(filePath)
if err != nil {
widgets.QMessageBox_Critical(
nil,
"Compilation error",
err.Error(),
widgets.QMessageBox__Ok,
widgets.QMessageBox__Ok,
)
return
}
deckListLabel.SetText(fmt.Sprintf("Mdem: %s", filePath))
start := time.Now().UnixMicro()
questions, err = api.Compile(string(fileContents))
if err != nil {
var errText string
switch err.(type) {
case comperror.PositionErr:
errText = fmt.Sprintf(
"%s on line %d, column %d",
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
}
duration := float32(time.Now().UnixMicro()-start) / 1000
fmt.Printf("Compilation took %.3fms", duration)
MakePages()
SwitchPage(0)
}
func main() {
// InitApp
app := widgets.NewQApplication(len(os.Args), os.Args)
window := widgets.NewQMainWindow(nil, 0)
hSplitter := widgets.NewQSplitter(nil)
window.SetWindowTitle("MDemory")
window.SetMinimumSize2(400, 300)
leftWidget := widgets.NewQWidget(nil, 0)
leftLayout := widgets.NewQVBoxLayout()
mdemLabel := widgets.NewQLabel2("Mdems", nil, 0)
model := widgets.NewQFileSystemModel(nil)
mdemList := widgets.NewQTreeView(nil)
leftWidget.SetLayout(leftLayout)
leftLayout.AddWidget(mdemLabel, 0, 0)
model.SetRootPath(workingPath)
mdemList.SetModel(model)
rootIndex := model.Index2("/home/jorenchik/Code/mdemory/memorybase", 0)
fmt.Printf("Root index: %s\n", model.FileName(rootIndex))
mdemList.SetRootIndex(rootIndex)
mdemList.HideColumn(1)
mdemList.HideColumn(2)
mdemList.HideColumn(3)
mdemList.ConnectDoubleClicked(
func(index *core.QModelIndex) {
OpenModelFile(model, index)
},
)
deckLabel := widgets.NewQLabel2("Decks", nil, 0)
deckList := widgets.NewQListView(nil)
leftLayout.AddWidget(mdemList, 1, 0)
leftLayout.AddWidget(deckLabel, 0, 0)
leftLayout.AddWidget(deckList, 1, 0)
rightWidget := widgets.NewQWidget(nil, 0)
rightLayout := widgets.NewQVBoxLayout()
rightWidget.SetLayout(rightLayout)
top := widgets.NewQWidget(nil, 0)
hTop := widgets.NewQHBoxLayout()
deckListLabel = widgets.NewQLabel(nil, 0)
top.SetLayout(hTop)
rightLayout.AddWidget(top, 0, 0)
hTop.AddWidget(deckListLabel, 0, 0)
hTop.AddStretch(1)
refresh := widgets.NewQToolButton(nil)
practice := widgets.NewQToolButton(nil)
shuffle := widgets.NewQToolButton(nil)
hTop.AddWidget(refresh, 0, 0)
hTop.AddWidget(shuffle, 0, 0)
hTop.AddWidget(practice, 0, 0)
refresh.SetText("Refresh")
shuffle.SetText("Shuffle")
shuffle.ConnectClicked(
func(checked bool) {
for i := range questions {
j := rand.Intn(i + 1)
questions[i], questions[j] = questions[j], questions[i]
}
SwitchPage(0)
},
)
practice.SetText("Practice")
mdemScroll := widgets.NewQScrollArea(nil)
mdemContainer := widgets.NewQWidget(nil, 0)
hMdemScroll = widgets.NewQVBoxLayout()
mdemScroll.SetWidget(mdemContainer)
mdemScroll.SetWidgetResizable(true)
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++ {
mdem := CreateMdem()
mdems = append(
mdems,
mdem,
)
hMdemScroll.AddWidget(mdem.wMdem, 0, 0)
}
mdemSpacer = widgets.NewQSpacerItem(
40,
40,
widgets.QSizePolicy__Minimum,
widgets.QSizePolicy__Expanding,
)
hMdemScroll.AddItem(mdemSpacer)
// CreateMdems
rightLayout.AddWidget(mdemScroll, 1, 0)
// MakePagination
pagination := widgets.NewQWidget(nil, 0)
hPagination := widgets.NewQHBoxLayout()
pagination.SetLayout(hPagination)
firstButton = widgets.NewQToolButton(nil)
firstButton.SetText("<<")
hPagination.AddWidget(firstButton, 0, 0)
firstButton.Hide()
firstButton.ConnectClicked(func(checked bool) {
if len(pages) > 0 {
SwitchPage(0)
}
})
prevButton = widgets.NewQToolButton(nil)
prevButton.SetText("<")
hPagination.AddWidget(prevButton, 0, 0)
prevButton.Hide()
prevButton.ConnectClicked(func(checked bool) {
if len(pages) > 0 {
SwitchPage(currentPage - 1)
}
})
for i := 0; i < PAGINATION_COUNT; i++ {
elButton := widgets.NewQToolButton(nil)
elButton.SetText(fmt.Sprintf("%d", i+1))
hPagination.AddWidget(elButton, 0, 0)
elButton.Hide()
elButton.ConnectClicked(func(checked bool) {
pageNum, err := strconv.ParseInt(elButton.Text(), 10, 64)
_ = err
pageIdx := int(pageNum) - 1
if (pageIdx < len(pages)) {
SwitchPage(pageIdx)
}
})
paginationButtons = append(
paginationButtons,
elButton,
)
}
nextButton = widgets.NewQToolButton(nil)
nextButton.SetText(">")
hPagination.AddWidget(nextButton, 0, 0)
nextButton.Hide()
nextButton.ConnectClicked(func(checked bool) {
if len(pages) > 0 {
SwitchPage(currentPage + 1)
}
})
lastButton = widgets.NewQToolButton(nil)
lastButton.SetText(">>")
hPagination.AddWidget(lastButton, 0, 0)
lastButton.Hide()
lastButton.ConnectClicked(func(checked bool) {
if len(pages) > 0 {
SwitchPage(len(pages) - 1)
}
})
hPagination.AddStretch(1)
paginationLabel = widgets.NewQLabel(nil, 0)
hPagination.AddWidget(paginationLabel, 0, 0)
rightLayout.AddWidget(pagination, 0, 0)
hSplitter.AddWidget(leftWidget)
hSplitter.AddWidget(rightWidget)
hSplitter.SetStretchFactor(0, 1)
hSplitter.SetStretchFactor(1, 3)
window.SetCentralWidget(hSplitter)
window.Show()
app.Exec()
}