diff --git a/src/compiler/parser/parser.go b/src/compiler/parser/parser.go index 5364d8e..ed8bda4 100644 --- a/src/compiler/parser/parser.go +++ b/src/compiler/parser/parser.go @@ -2,44 +2,44 @@ package parser import ( "fmt" - "strings" - "log" "github.com/jorenchik/mdemory/src/compiler/lexer" + "log" + "strings" ) type Question interface { - ToString() string; + ToString() string } type SingleAnswerQuestion struct { - id string; - question string; - answer string; - section string; + ID string + Question string + Answer string + Section string } type Choice struct { - answer string; - isCorrect bool; + answer string + isCorrect bool } type MultipleChoiceQuestion struct { - id string; - question string; - choices []Choice; - section string; + id string + question string + choices []Choice + section string } -func (question SingleAnswerQuestion)ToString() string { +func (question SingleAnswerQuestion) ToString() string { return fmt.Sprintf( " (%s) %s: %s\n", - question.section, - strings.Trim(question.question, "\t\n "), - strings.Trim(question.answer, "\t\n "), + question.Section, + strings.Trim(question.Question, "\t\n "), + strings.Trim(question.Answer, "\t\n "), ) } -func (question MultipleChoiceQuestion)ToString() string { +func (question MultipleChoiceQuestion) ToString() string { acc := "" acc += fmt.Sprintf( " (%s) %s\n", @@ -48,7 +48,7 @@ func (question MultipleChoiceQuestion)ToString() string { ) for _, el := range question.choices { opener := '-' - if (el.isCorrect) { + if el.isCorrect { opener = '+' } acc += fmt.Sprintf("\t%c %s\n", opener, strings.Trim(el.answer, "\t\n ")) @@ -57,16 +57,16 @@ func (question MultipleChoiceQuestion)ToString() string { } type QuestionElement struct { - isDash bool; - content string; -} + isDash bool + content string +} -var automata map[lexer.TokenType][]lexer.TokenType +var automata map[lexer.TokenType][]lexer.TokenType type CompilerErr struct { - message string; - row int32; - column int32; + message string + row int32 + column int32 } func (e CompilerErr) Error() string { @@ -74,12 +74,12 @@ func (e CompilerErr) Error() string { } func contains(s []lexer.TokenType, e lexer.TokenType) bool { - for _, a := range s { - if a == e { - return true - } - } - return false + for _, a := range s { + if a == e { + return true + } + } + return false } func parserAutomata() map[lexer.TokenType][]lexer.TokenType { @@ -95,22 +95,22 @@ func parserAutomata() map[lexer.TokenType][]lexer.TokenType { } 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, } @@ -129,30 +129,30 @@ func parserAutomata() map[lexer.TokenType][]lexer.TokenType { func ValidateGrammar(tokens []lexer.Token) error { automata = parserAutomata() - for i := 0; i < len(tokens) - 1; i++ { + for i := 0; i < len(tokens)-1; i++ { token := tokens[i] - nextToken := tokens[i + 1] - if (!contains(automata[token.TokenType], nextToken.TokenType)) { + nextToken := tokens[i+1] + if !contains(automata[token.TokenType], nextToken.TokenType) { return CompilerErr{ message: fmt.Sprintf( "Token %s cannot precede %s\n", lexer.ToString(&token.TokenType), lexer.ToString(&nextToken.TokenType), ), - row: token.Row, + row: token.Row, column: token.Column, } } - } + } return nil } func ParseQuestions(fileContents string) ([]Question, error) { tokens, err := lexer.TokenizeMdem([]rune(fileContents)) - if (err != nil) { + if err != nil { return nil, err } - if (true) { + if true { log.Println("Lexer output:") for _, el := range tokens { fmt.Print(el.ToString()) @@ -160,7 +160,7 @@ func ParseQuestions(fileContents string) ([]Question, error) { } err = ValidateGrammar(tokens) - if (err != nil) { + if err != nil { log.Fatal(err.Error()) } @@ -168,26 +168,26 @@ func ParseQuestions(fileContents string) ([]Question, error) { section := "" i := 0 for { - if (i >= len(tokens)) { + if i >= len(tokens) { break } - if (tokens[i].TokenType == lexer.ElementDashStart) { - id := tokens[i + 2].Content - question := tokens[i + 4].Content + if tokens[i].TokenType == lexer.ElementDashStart { + id := tokens[i+2].Content + question := tokens[i+4].Content quesitonElements := []QuestionElement{} i += 6 for { - if (i + 1 >= len(tokens) || + if i+1 >= len(tokens) || !(tokens[i].TokenType == lexer.ElementDashStart || tokens[i].TokenType == lexer.ElementPlusStart) || - tokens[i+1].TokenType == lexer.IdentifierStart) { + tokens[i+1].TokenType == lexer.IdentifierStart { break } questionElement := QuestionElement{} - if (tokens[i].TokenType == lexer.ElementDashStart) { - questionElement.isDash = true + if tokens[i].TokenType == lexer.ElementDashStart { + questionElement.isDash = true } else { - questionElement.isDash = false + questionElement.isDash = false } questionElement.content = tokens[i+1].Content quesitonElements = append(quesitonElements, questionElement) @@ -195,7 +195,7 @@ func ParseQuestions(fileContents string) ([]Question, error) { } if len(quesitonElements) > 1 { question := MultipleChoiceQuestion{ - id: id, + id: id, question: question, } choices := []Choice{} @@ -204,30 +204,30 @@ func ParseQuestions(fileContents string) ([]Question, error) { choice.answer = quesitonElements[k].content choice.isCorrect = !quesitonElements[k].isDash choices = append(choices, choice) - } - if (section != "") { + } + if section != "" { question.section = section } question.choices = choices questions = append(questions, question) - } else if (len(quesitonElements) == 1) { + } else if len(quesitonElements) == 1 { question := SingleAnswerQuestion{ - id: id, - question: question, - answer: quesitonElements[0].content, + ID: id, + Question: question, + Answer: quesitonElements[0].content, } - if (section != "") { - question.section = section + if section != "" { + question.Section = section } questions = append(questions, question) } - } else if (tokens[i].TokenType == lexer.SectionIdentifierStart) { - section = tokens[i + 1].Content - i += 3; - } else if (tokens[i].TokenType == lexer.SectionEnd) { + } else if tokens[i].TokenType == lexer.SectionIdentifierStart { + section = tokens[i+1].Content + i += 3 + } else if tokens[i].TokenType == lexer.SectionEnd { section = "" i += 1 - } else if (tokens[i].TokenType == lexer.EOF) { + } else if tokens[i].TokenType == lexer.EOF { break } else { log.Fatalf( @@ -236,8 +236,8 @@ func ParseQuestions(fileContents string) ([]Question, error) { ) return nil, CompilerErr{ message: "", - row: tokens[i].Row, - column: tokens[i].Column, + row: tokens[i].Row, + column: tokens[i].Column, } } } diff --git a/src/mdemory-app/.vscode/launch.json b/src/mdemory-app/.vscode/launch.json new file mode 100644 index 0000000..54749c4 --- /dev/null +++ b/src/mdemory-app/.vscode/launch.json @@ -0,0 +1,16 @@ + +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Wails: build debug", + "type": "go", + "request": "launch", + "mode": "exec", + "program": "${workspaceFolder}/myapp", + "preLaunchTask": "wails_debug_build", + "env": {}, + "args": [] + } + ] +} diff --git a/src/mdemory-app/.vscode/tasks.json b/src/mdemory-app/.vscode/tasks.json new file mode 100644 index 0000000..ab108d5 --- /dev/null +++ b/src/mdemory-app/.vscode/tasks.json @@ -0,0 +1,11 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "wails_debug_build", + "type": "shell", + "command": "wails build -debug" + } + ] +} + diff --git a/src/mdemory-app/app.go b/src/mdemory-app/app.go index 0cefa2d..a30ed3a 100644 --- a/src/mdemory-app/app.go +++ b/src/mdemory-app/app.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "github.com/jorenchik/mdemory/src/compiler/parser" "os" "regexp" ) @@ -28,30 +29,76 @@ func (a *App) Greet(name string) string { return fmt.Sprintf("Hello %s, It's show time!", name) } -type File struct{ - Name string; - IsDir bool; +type File struct { + Name string + IsDir bool } -// Greet returns a greeting for the given name -func (a *App) GetRepoFiles() []File { - dirPath := "/home/jorenchik/Code/mdemory/memorybase/" +func (a *App) GetRepoFiles(dirPath string) []File { dirEntries, err := os.ReadDir(dirPath) - if (err != nil) { + if err != nil { return []File{} } files := []File{} exp, err := regexp.Compile(".+\\.mdem") for _, el := range dirEntries { - if (err != nil) { + if err != nil { continue } - if (el.IsDir() || exp.Match([]byte(el.Name()))) { + if el.IsDir() || exp.Match([]byte(el.Name())) { file := File{} file.Name = el.Name() file.IsDir = el.IsDir() files = append(files, file) } } - return files + return files +} + +func (a *App) ParseFile(filePath string) (string, error) { + fmt.Printf("Parse file requested. Path: %s\n", filePath) + dirName := "/home/jorenchik/Code/mdemory/memorybase/" + dir, err := os.ReadDir(dirName) + + for _, el := range dir { + if err != nil { + continue + } + path := dirName + el.Name() + fmt.Println(path) + bytes, err := os.ReadFile(path) + if err != nil { + continue + } + fmt.Println(string(bytes)) + } + + bytes, err := os.ReadFile(filePath) + if err != nil { + fmt.Printf("File opening error: %s\n", err.Error()) + return "", err + } + + fileContents := string(bytes) + questions, err := parser.ParseQuestions(fileContents) + + if err != nil { + fmt.Printf("Parsing error: %s\n", err.Error()) + return "", err + } + + for i := 0; i < len(questions); i++ { + question := questions[i] + switch q := question.(type) { + case parser.SingleAnswerQuestion: + fmt.Printf( + "Single Answer: question: %s; answer: %s", + q.Question, + q.Answer, + ) + } + } + + fmt.Printf("Returning file. Path: %s\n", filePath) + return fileContents, nil } diff --git a/src/mdemory-app/frontend/index.html b/src/mdemory-app/frontend/index.html index f80af2b..c643685 100644 --- a/src/mdemory-app/frontend/index.html +++ b/src/mdemory-app/frontend/index.html @@ -1,75 +1,119 @@ + - - + + mdemory-app + -
-
-
-

memorybase

-
- -
-
- - - - - - - - - - - - -
-
- -
-
-
-

flashcards: countries

-
-
- > - What is the capital of Latvia? -
-
- > - What is the capital of Lithuania? -
-
-
-
-
- +
+
+
+ Base +
+
Save
+
Open
+
Reload
+
+
+
+ Help +
+
+
+
+
+

memorybase

+ +
+
+ +
+ + + + + + + + + + + + +
+
+ +
+
+

countries.mdem

+
+
+
+ Flashcards: 12. + Page: 1 of 3. +
+
+ + +
+
+
+
+
+ > + What is the capital of Latvia? +
+
+
+
+ > + What is the capital of Lithuania? +
+
+
+
+ > + What is the capital of Estonia? +
+
+ - + Tallin +
+
+
+
+
+
+
+ + diff --git a/src/mdemory-app/frontend/src/app.css b/src/mdemory-app/frontend/src/app.css index 9a14860..7542beb 100644 --- a/src/mdemory-app/frontend/src/app.css +++ b/src/mdemory-app/frontend/src/app.css @@ -1,26 +1,60 @@ -.main-layout { - display: grid; - grid-template-columns: .35fr 1fr; /* Creates 3 equal columns */ - height: 100%; +html { + background-color: rgba(27, 38, 54, 1); + text-align: center; + color: white; } -.main-layout > div:nth-child(1) { - /*background: white;*/ - border-right: 1px solid gray; -} - -.main-layout .deck-list { - padding: .5rem 1rem; - display: flex; - flex-direction: column; - /*gap: .5rem;*/ +body { + box-sizing: content-box; + margin: 0; + color: white; + font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; } a { - color: unset; - text-decoration: unset; + color: unset; + text-decoration: unset; } +h1, h2, h3, h4 { + font-size: 1.1rem; +} + +#app { + text-align: center; + height: 100vh; + display: flex; + flex-direction: column; +} + +@font-face { + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + src: local(""), + url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2"); +} + +.main-layout { + display: grid; + grid-template-columns: .35fr 1fr; + flex-grow: 1; + /* height: 100%; */ +} + +.main-layout > div:nth-child(1) { + border-right: 1px solid gray; +} + +.main-layout .file-menus { + padding: 0 20px; + display: flex; + flex-direction: column; +} + + .main-layout .deck { border: 1px solid gray; border-top: none; @@ -36,30 +70,55 @@ a { border-top: 1px solid gray; } -h1, h2, h3, h4 { - font-size: 1.1rem; -} - - .deck-listing { display: flex; flex-direction: column; - padding: 1rem; gap: 1rem; } +.deck-listing-top { + display: flex; + justify-content: space-between; + width: 100%; + margin: 20px 0; +} + +.deck-listing-box { + padding: 0 20px; +} + .deck-listing .flashcard { display: flex; align-items: center; - border: 1px solid white; height: 80px; - border-radius: .3rem; + border: 1px solid white; + border-top-left-radius: .3rem; + border-top-right-radius: .3rem; + border-bottom-left-radius: .3rem; + border-bottom-right-radius: .3rem; } .deck-listing .flashcard > * { margin-left: 15px; } +.deck-listing .answer { + margin-top: -2px; + display: flex; + align-items: center; + height: 80px; + border: 1px solid white; + border-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: .3rem; + border-bottom-right-radius: .3rem; +} + +.deck-listing .answer > * { + padding-left: 15px; +} + .repo-input { width: 99%; height: 25px; @@ -68,11 +127,19 @@ h1, h2, h3, h4 { .mbase-actions { display: flex; + gap: 0rem; + flex-direction: column; align-items: center; - gap: .5rem; margin-bottom: 1rem; } +.mbase-search { + display: flex; + gap: .5rem; + align-items: center; + width: 100%; +} + .mbase-actions a { font-size: .8rem; display: block; @@ -82,11 +149,72 @@ h1, h2, h3, h4 { font-size: .9rem; display: flex; margin: 0; - padding: 0 1rem; + padding: 10px 0; } +.file-menus { + height: 100%; +} + .file-menus > div { margin-bottom: 1.5rem; - +} + +.mbase-actions { + height: 10%; +} + +.mbase-mdems { + max-height: 40%; +} + +.mbase-decks { + max-height: 40%; +} + +.top-menu { + position: sticky; + display: flex; + width: 100%; + background: gray; + border: 1px solid white; +} + +.top-menu > div { + cursor: pointer; + border-right: 1px solid white; + padding: 1px 10px; +} + +.top-menu .option { + position: relative; +} + +.top-menu .option .list { + position: absolute; + top: 100%; + left: 0; + background: gray; + width: 150px; + /* display: flex; */ + display: none; + flex-direction: column; + text-align: left; + padding: 0; + margin: 0; + border: 1px solid white; +} + +.top-menu .option .list > div { + padding: 0 0 0 15px; + margin: 0; +} + +.top-menu .option .list > div:nth-child(1) { + border-top: 0; +} + +.top-menu .option .list > div { + border-top: 1px solid white; } diff --git a/src/mdemory-app/frontend/src/main.ts b/src/mdemory-app/frontend/src/main.ts index 6502f1e..64bc28c 100644 --- a/src/mdemory-app/frontend/src/main.ts +++ b/src/mdemory-app/frontend/src/main.ts @@ -1,7 +1,7 @@ import './style.css'; import './app.css'; -import {GetRepoFiles} from '../wailsjs/go/main/App'; +import {GetRepoFiles, ParseFile} from '../wailsjs/go/main/App'; import {main} from '../wailsjs/go/models'; interface DeckFile { @@ -9,49 +9,56 @@ interface DeckFile { IsDir: boolean; } +// console.log(await ParseFile("fjoewjf")); + function fetchFiles() { - GetRepoFiles().then((result: main.File[]) => { + GetRepoFiles("/home/jorenchik/Code/mdemory/memorybase/") + .then((result: main.File[]) => { const files: DeckFile[] = result as DeckFile[] setDecklistFiles(files) }); } fetchFiles() +let mbasePath = "/home/jorenchik/Code/mdemory/memorybase/" + +async function fetchParsed(filename: string) { + console.log(filename); + try { + console.log(await ParseFile(mbasePath + filename)); + } catch (error) { + console.error("Error:", error); + } + // let time = new Date().getTime(); + // console.log(await ParseFile(mbasePath + filename)); + // console.log(new Date().getTime() - time) +} + let decklist: Element | null = document.querySelector("#deck-list"); function setDecklistFiles(files: DeckFile[]) { if (decklist) { + // removeAnchors let deckFileAnchors = decklist.querySelectorAll(".deck-link") deckFileAnchors.forEach(i => { i.remove(); }); + + // createAnchors for (let i = 0; i < files.length; i++) { let element = document.createElement('a'); - let angleHtml = '' - if (files[i].IsDir) { - angleHtml = ">" - } - element.setAttribute("href", "#") - element.setAttribute("class", "deck-link") element.innerHTML = `
- ${ angleHtml } ${files[i].Name}
` + element.setAttribute("href", "#") + element.setAttribute("class", "deck-link") decklist.appendChild(element) - if (files[i].IsDir) { - let angle: HTMLElement | null = element.querySelector("[angle='true']") - element.addEventListener("click", _clickable => { - let style: CSSStyleDeclaration | undefined = angle?.style - if (style) { - if (style["rotate"] == "90deg") { - style["rotate"] = ""; - } else { - style["rotate"] = "90deg"; - } - } - }) - } + + element.addEventListener('click', _ => { + fetchParsed(element.innerText); + }); + } } } diff --git a/src/mdemory-app/frontend/src/style.css b/src/mdemory-app/frontend/src/style.css index 3940d6c..e69de29 100644 --- a/src/mdemory-app/frontend/src/style.css +++ b/src/mdemory-app/frontend/src/style.css @@ -1,26 +0,0 @@ -html { - background-color: rgba(27, 38, 54, 1); - text-align: center; - color: white; -} - -body { - margin: 0; - color: white; - font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", - "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; -} - -@font-face { - font-family: "Nunito"; - font-style: normal; - font-weight: 400; - src: local(""), - url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2"); -} - -#app { - height: 100vh; - text-align: center; -} diff --git a/src/mdemory-app/frontend/wailsjs/go/main/App.d.ts b/src/mdemory-app/frontend/wailsjs/go/main/App.d.ts index 18342c7..cf222c9 100755 --- a/src/mdemory-app/frontend/wailsjs/go/main/App.d.ts +++ b/src/mdemory-app/frontend/wailsjs/go/main/App.d.ts @@ -2,6 +2,8 @@ // This file is automatically generated. DO NOT EDIT import {main} from '../models'; -export function GetRepoFiles():Promise>; +export function GetRepoFiles(arg1:string):Promise>; export function Greet(arg1:string):Promise; + +export function ParseFile(arg1:string):Promise; diff --git a/src/mdemory-app/frontend/wailsjs/go/main/App.js b/src/mdemory-app/frontend/wailsjs/go/main/App.js index 14aeddf..5b05e6c 100755 --- a/src/mdemory-app/frontend/wailsjs/go/main/App.js +++ b/src/mdemory-app/frontend/wailsjs/go/main/App.js @@ -2,10 +2,14 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -export function GetRepoFiles() { - return window['go']['main']['App']['GetRepoFiles'](); +export function GetRepoFiles(arg1) { + return window['go']['main']['App']['GetRepoFiles'](arg1); } export function Greet(arg1) { return window['go']['main']['App']['Greet'](arg1); } + +export function ParseFile(arg1) { + return window['go']['main']['App']['ParseFile'](arg1); +} diff --git a/src/mdemory-app/go.mod b/src/mdemory-app/go.mod index 09b32f0..6fcaa8b 100644 --- a/src/mdemory-app/go.mod +++ b/src/mdemory-app/go.mod @@ -2,7 +2,10 @@ module mdemory-app go 1.22.5 -require github.com/wailsapp/wails/v2 v2.9.1 +require ( + github.com/jorenchik/mdemory/src/compiler v0.0.0-00010101000000-000000000000 + github.com/wailsapp/wails/v2 v2.9.1 +) require ( github.com/bep/debounce v1.2.1 // indirect