package main import ( "fmt" "log" "os" "strings" ) type Question interface {} 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; } type QuestionElement struct { isDash bool; content string; } var automata map[TokenType][]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 []TokenType, e TokenType) bool { for _, a := range s { if a == e { return true } } return false } func parserAutomata() map[TokenType][]TokenType { automata := make(map[TokenType][]TokenType) automata[TextFragment] = []TokenType{ QuestionEnd, ElementDashStart, ElementPlusStart, SectionIdentifierStart, SectionStart, EOF, SectionEnd, } automata[QuestionEnd] = []TokenType{ ElementDashStart, ElementPlusStart, } automata[ElementDashStart] = []TokenType{ IdentifierStart, TextFragment, } automata[ElementPlusStart] = []TokenType{ TextFragment, } automata[Identifier] = []TokenType{ IdentifierEnd, SectionStart, } automata[IdentifierStart] = []TokenType{ Identifier, } automata[IdentifierEnd] = []TokenType{ TextFragment, } automata[SectionIdentifierStart] = []TokenType{ Identifier, } automata[SectionStart] = []TokenType{ ElementDashStart, SectionIdentifierStart, EOF, } automata[SectionEnd] = []TokenType{ SectionIdentifierStart, ElementDashStart, EOF, } automata[SOF] = []TokenType{ ElementDashStart, SectionIdentifierStart, EOF, } automata[EOF] = []TokenType{} return automata } func validateGrammar() error { for i := 0; i < len(tokens) - 1; i++ { token := tokens[i] content := token.content if (token.tokenType == TextFragment) { content = strings.Replace( strings.Trim(content, " "), "\n", "\\n", -1, ) } nextToken := tokens[i + 1] fmt.Printf( "%s: \"%s\" %d:%d\n", ToString(&token.tokenType), content, token.row, token.column, ) if (false) { fmt.Print("Possible next tokens:") for k:=0; k= len(tokens)) { break } if (tokens[i].tokenType == ElementDashStart) { id := tokens[i + 2].content question := tokens[i + 4].content quesitonElements := []QuestionElement{} i += 6 for { if (i + 1 >= len(tokens) || !(tokens[i].tokenType == ElementDashStart || tokens[i].tokenType == ElementPlusStart) || tokens[i+1].tokenType == IdentifierStart) { break } questionElement := QuestionElement{} if (tokens[i].tokenType == ElementDashStart) { questionElement.isDash = true } else { questionElement.isDash = false } questionElement.content = tokens[i+1].content quesitonElements = append(quesitonElements, questionElement) i += 2 } if len(quesitonElements) > 1 { question := MultipleChoiceQuestion{ id: id, question: question, } choices := []Choice{} for k := 0; k < len(quesitonElements); k++ { choice := Choice{} choice.answer = quesitonElements[k].content choice.isCorrect = !quesitonElements[k].isDash choices = append(choices, choice) } if (section != "") { question.section = section } question.choices = choices questions = append(questions, question) } else if (len(quesitonElements) == 1) { question := SingleAnswerQuestion{ id: id, question: question, answer: quesitonElements[0].content, } if (section != "") { question.section = section } questions = append(questions, question) } } else if (tokens[i].tokenType == SectionIdentifierStart) { section = tokens[i + 1].content i += 3; } else if (tokens[i].tokenType == SectionEnd) { section = "" i += 1 } else if (tokens[i].tokenType == EOF) { break } else { log.Fatalf( "Not handled: %s", ToString(&tokens[i].tokenType), ) return nil, CompilerErr{ message: "", row: tokens[i].row, column: tokens[i].column, } } } return questions, nil } func main() { log.Println("Compilation started") file, err := os.ReadFile("/home/jorenchik/Code/mdemory/src/compiler/input.mdem") if (err != nil) { log.Fatalf("Cannot open the input file: %s", err.Error()) return } fileContents := string(file) tokens, err := tokenize([]rune(fileContents)) if (err != nil) { fmt.Printf("%s\n", err.Error()) return } automata = parserAutomata() err = validateGrammar() if (err != nil) { log.Fatal(err.Error()) } questions, err := ParseQuestions(tokens) if (err != nil) { log.Fatal(err.Error()) } for _, element := range questions { switch element.(type) { case SingleAnswerQuestion: fmt.Printf( " (%s) %s: %s\n", element.(SingleAnswerQuestion).section, strings.Trim(element.(SingleAnswerQuestion).question, "\t\n "), strings.Trim(element.(SingleAnswerQuestion).answer, "\t\n "), ) case MultipleChoiceQuestion: fmt.Printf( " (%s) %s\n", element.(MultipleChoiceQuestion).section, element.(MultipleChoiceQuestion).question, ) for _, el := range element.(MultipleChoiceQuestion).choices { opener := '-' if (el.isCorrect) { opener = '+' } fmt.Printf("\t%c %s\n", opener, strings.Trim(el.answer, "\t\n ")) } } } log.Println("Compilation completed") }