Finish variables

This commit is contained in:
conzer 2024-12-05 23:53:34 -05:00
parent 89fff8112f
commit 1f98b9c421
7 changed files with 164 additions and 88 deletions

View file

@ -3,12 +3,14 @@ package compiler
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"hylia/parser" "hylia/parser"
) )
// THE HYLIA COMPILER // THE HYLIA COMPILER
func Compile(elements []parser.Element, outputFile string) error { func Compile(elements []parser.Element, variables map[string]string, outputFile string) error {
var head string var head string
var body string var body string
@ -42,27 +44,11 @@ func Compile(elements []parser.Element, outputFile string) error {
return fmt.Errorf("Failed to write to the output file: %w", err) return fmt.Errorf("Failed to write to the output file: %w", err)
} }
if head != "" {
_, err = file.WriteString("<head>\n" + head + "</head>\n")
if err != nil {
return fmt.Errorf(errmsg, err)
}
}
if body != "" { err = writeElements(elements, file, variables)
_, err = file.WriteString("<body>\n" + body + "</body>\n") if err != nil {
if err != nil { return fmt.Errorf(errmsg, err)
return fmt.Errorf(errmsg, err)
}
} }
if other != "" {
_, err = file.WriteString(other)
if err != nil {
return fmt.Errorf(errmsg, err)
}
}
// Close the HTML structs // Close the HTML structs
_, err = file.WriteString("</html>\n") _, err = file.WriteString("</html>\n")
if err != nil { if err != nil {
@ -72,3 +58,65 @@ func Compile(elements []parser.Element, outputFile string) error {
return nil return nil
} }
func writeElements(elements []parser.Element, file *os.File, variables map[string]string) error {
for _, element := range elements {
if element.Name == "head" {
_, err := file.WriteString(fmt.Sprintf("<head>\n"))
if err != nil {
return fmt.Errorf("Failed to write element %s: %w", element.Name, err)
}
}
if element.Name == "body" {
_, err := file.WriteString(fmt.Sprintf("<body>\n"))
if err != nil {
return fmt.Errorf("Failed to write element %s: %w", element.Name, err)
}
}
content := replaceVariables(element.Content, variables)
if strings.TrimSpace(content) != "" {
_, err := file.WriteString(strings.TrimSpace(content) + "\n")
if err != nil {
return err
}
}
// recursively process nested elements
if element.NestedElements != nil && len(element.NestedElements) > 0 {
err := writeElements(element.NestedElements, file, variables)
if err != nil {
return err
}
}
if element.Name == "head" {
_, err := file.WriteString(fmt.Sprintf("</head>\n"))
if err != nil {
return fmt.Errorf("Failed to write element %s: %w", element.Name, err)
}
}
if element.Name == "body" {
_, err := file.WriteString(fmt.Sprintf("</body>\n"))
if err != nil {
return fmt.Errorf("Failed to write element %s: %w", element.Name, err)
}
}
}
return nil
}
func replaceVariables(content string, variables map[string]string) string {
for key, value := range variables {
placeholder := fmt.Sprintf("{{%s}}", key)
content = strings.ReplaceAll(content, placeholder, value)
}
return content
}

View file

@ -1,10 +1,11 @@
<hylia> <hylia>
<import file="header.hy"></import>
<element name="head"> <element name="head">
<title>Hello Hylia!</title> <title>Hello Hylia!</title>
</element> </element>
<var name="testvar"><h1>Hello!</h1><br><h2>Hello again!</h2></var>
<element name="body"> <element name="body">
<header /> <h1>Hello Hylia!</h1>
<p>This is the main content.</p> <p>This is an example content.</p>
{{testvar}}
</element> </element>
</hylia> </hylia>

11
examples/output.htm Normal file
View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Hello Hylia!</title>
</head>
<body>
<h1>Hello Hylia!</h1>
<p>This is an example content.</p>
<h1>Hello!</h1><br><h2>Hello again!</h2>
</body>
</html>

View file

@ -1,18 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Hello Hylia!</title>
<title>Hello Hylia!</title>
</head>
<body> <body>
<header>
<header /> <h1>Hello Hylia!</h1>
<p>This is the main content.</p> <p>
<p>This is an example content.</p>
</body>
<h1> Welcome to Hylia! </h1>
<p>This is a reusable header component.</p>
</html> </html>

BIN
hylia

Binary file not shown.

View file

@ -16,13 +16,13 @@ func main() {
inputFile := os.Args[1] inputFile := os.Args[1]
outputFile := os.Args[2] outputFile := os.Args[2]
parsedElememts, err := parser.ParseFile(inputFile) parsedElememts, variables, err := parser.ParseFile(inputFile)
if err != nil { if err != nil {
fmt.Printf("ERROR! %v\n", err) fmt.Printf("ERROR! %v\n", err)
os.Exit(1) os.Exit(1)
} }
err = compiler.Compile(parsedElememts, outputFile) err = compiler.Compile(parsedElememts, variables, outputFile)
if err != nil { if err != nil {
fmt.Printf("ERROR! %v\n", err) fmt.Printf("ERROR! %v\n", err)
os.Exit(1) os.Exit(1)

View file

@ -10,65 +10,89 @@ import (
// THE HYLIA PARSER // THE HYLIA PARSER
type Element struct { type Element struct {
Name string Name string
Content string Content string
FilePath string FilePath string
NestedElements []Element
Variables map[string]string
} }
func ParseFile(filename string) ([]Element, error) { func ParseFile(filename string) ([]Element, map[string]string, error) {
// Hey, does the file we're trying to parse actually exist? // Hey, does the file we're trying to parse actually exist?
data, err := ioutil.ReadFile(filename) data, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// Parse the file, and ask "Is this a Hylia file?" elements, variables, err := parseElements(string(data), filename)
content := string(data) if err != nil {
if !strings.Contains(content, "<hylia>")|| !strings.Contains(content, "</hylia>") { return nil, nil, fmt.Errorf("failed to parse elements: %w", err)
return nil, errors.New("The structure of this file is invalid. Are you sure this is a Hylia (.hy) file?")
} }
// Extract the actual Hylia content return elements, variables, nil
start := strings.Index(content, "<hylia>")
end := strings.Index(content, "</hylia>")
if start == -1 || end == -1 {
return nil, errors.New("Missing <hylia> tags. Are you sure this is a Hylia (.hy) file?")
}
content = content[start+len("<hylia>") : end] }
// Extract the custom elements, which are wrapped in <element> tags and handle imports func parseElements(content, filename string) ([]Element, map[string]string, error) {
elements := []Element{} elements := []Element{}
lines := strings.Split(content, "\n") variables := make(map[string]string)
for _, line := range lines {
line = strings.TrimSpace(line) lines := strings.Split(content, "\n")
if strings.HasPrefix(line, "<element") { var currentElement *Element
// Extract the element name var nestedContent strings.Builder
name := extractAttributeValue(line, "name")
if name == "" { for _, line := range lines {
return nil, errors.New("<element> tag is missing a name. ('name' attribute)") line = strings.TrimSpace(line)
if strings.HasPrefix(line, "<var ") {
name := extractAttributeValue(line, "name")
if name == "" {
return nil, nil, errors.New("Variable is missing a name ('name' attribute)")
}
valueStart := strings.Index(line, ">")
valueEnd := strings.Index(line, "</var>")
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, "<element") {
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()
}
name := extractAttributeValue(line, "name")
if name == "" {
return nil, nil, errors.New("Element is missing a name ('name' attribute)")
}
currentElement = &Element{Name: name, FilePath: filename}
} else if strings.HasPrefix(line, "</element>") {
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 currentElement != nil {
nestedContent.WriteString(line + "\n")
}
} }
content, err := extractElementContent(line, content)
if err != nil {
return nil, err
}
elements = append(elements, Element{Name: name, Content: content, FilePath: filename})
} else if strings.HasPrefix(line, "<import") {
filePath := extractAttributeValue(line, "file")
if filePath == "" {
return nil, errors.New("<import> tag is missing a file ('file' attribute)")
}
// Recursively parse
importedElements, err := ParseFile(filePath)
if err != nil {
return nil, fmt.Errorf("ERROR: Could not import file %s: %w", filePath, err)
}
elements = append(elements, importedElements...)
} }
return elements, variables, nil
}
func mergeVariables(target, source map[string]string) {
for key, value := range source {
target[key] = value
} }
return elements, nil
} }
// Extracts an attribute // Extracts an attribute