Finish variables
This commit is contained in:
parent
89fff8112f
commit
1f98b9c421
7 changed files with 164 additions and 88 deletions
|
@ -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
|
||||||
|
}
|
|
@ -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
11
examples/output.htm
Normal 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>
|
|
@ -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
BIN
hylia
Binary file not shown.
4
main.go
4
main.go
|
@ -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)
|
||||||
|
|
114
parser/parser.go
114
parser/parser.go
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue