package parser import ( "errors" "fmt" "io/ioutil" "strings" "path/filepath" ) // THE HYLIA PARSER type Element struct { Name string Content string FilePath string NestedElements []Element Variables map[string]string } func ParseFile(filename string) ([]Element, map[string]string, error) { // Hey, does the file we're trying to parse actually exist? data, err := ioutil.ReadFile(filename) if err != nil { return nil, nil, err } elements, variables, err := parseElements(string(data), filename) if err != nil { return nil, nil, fmt.Errorf("failed to parse elements: %w", err) } importedElements := []Element{} importedVariables := map[string]string{} for _, element := range elements { if element.Name == "import" { importPath := extractAttributeValue("import", "src") if importPath == "" { return nil, nil, fmt.Errorf("Import is missing a source ('src' attribute)") } importFullPath := resolvePath(filename, importPath) childElements, childVariables, err := ParseFile(importFullPath) if err != nil { return nil, nil, fmt.Errorf("failed to parse elements: %w", err) } importedElements = append(importedElements, childElements...) for k, v := range childVariables { importedVariables[k] = v } } } for k, v := range importedVariables { if _, exists := variables[k]; !exists { variables[k] = v } } elements = append(importedElements, elements...) return elements, variables, nil } func resolvePath(basePath, relativePath string) string { dir := filepath.Dir(basePath) return filepath.Join(dir, relativePath) } func parseElements(content, filename string) ([]Element, map[string]string, error) { elements := []Element{} variables := make(map[string]string) classes := make(map[string]Element) lines := strings.Split(content, "\n") var currentElement *Element var nestedContent strings.Builder for _, line := range lines { line = strings.TrimSpace(line) if strings.HasPrefix(line, "") valueEnd := strings.Index(line, "") if valueStart == -1 || valueEnd == -1 { return nil, nil, errors.New("invalid variable structure") } value := strings.TrimSpace(line[valueStart+1 : valueEnd]) variables[name] = value } else if strings.HasPrefix(line, "") { if currentElement != nil { currentElement.Content = nestedContent.String() parsedNested, nestedVars, _ := parseElements(nestedContent.String(), filename) currentElement.NestedElements = parsedNested mergeVariables(variables, nestedVars) elements = append(elements, *currentElement) nestedContent.Reset() currentElement = nil } } else if strings.HasPrefix(line, "") { if currentElement != nil { currentElement.Content = nestedContent.String() parsedNested, nestedVars, _ := parseElements(nestedContent.String(), filename) currentElement.NestedElements = parsedNested mergeVariables(variables, nestedVars) if classes == nil { classes = make(map[string]Element) } classes[currentElement.Name] = *currentElement nestedContent.Reset() currentElement = nil } } else { if currentElement != nil { nestedContent.WriteString(line + "\n") } } } return elements, variables, nil } func mergeVariables(target, source map[string]string) { for key, value := range source { target[key] = value } } // Extracts an attribute func extractAttributeValue(tag, attribute string) string { prefix := fmt.Sprintf(`%s="`, attribute) start := strings.Index(tag, prefix) if start == -1 { return "" } start += len(prefix) end := strings.Index(tag[start:], `"`) if end == -1 { return "" } return tag[start : start+end] } // Extracts an func extractElementContent(tag, content string) (string, error) { start := strings.Index(content, tag) if start == -1 { return "", errors.New("element tag not found in content") } end := strings.Index(content[start:], "") if end == -1 { return "", errors.New("missing end tag of element") } return content[start+len(tag) : start+end], nil }