AcuteML is an Acute Markup Language (AML) for Web/XML development in Julia.
It automatically creates or extracts HTML/XML files from Julia types!
It also has a general templating engine, which can be used for any type of documents.
using Pkg
Pkg.add("AcuteML")
using AcuteML
See Type Definition for a comprehensive introduction to syntax. You can use @aml
macro to define a Julia type, and then the package automatically creates a xml or html associated with the defined type.
using AcuteML
# the xml/html name of each property is written in front of it (e.g. "body")
# `~` means that the struct property name is the same as xml/html name
@aml mutable struct Body "body"
h1, "~"
p::Vector{String}, "~"
end
@aml mutable struct Page doc"html"
body::Body, "~"
end
b = Body(h1 = "My heading", p = ["Paragraph1", "Paragraph2"])
d = Page(body = b)
pprint(d)
julia> pprint(d)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<body>
<h1>My heading</h1>
<p>Paragraph1</p>
<p>Paragraph2</p>
</body>
</html>
More advanced Examples are given in the following:
First, we define the structs using @aml
to store the data in:
using AcuteML
# Types definition
# Person Type
@aml mutable struct Person "person", check_course
age::UInt64, "~"
field, "study-field"
GPA::Float64 = 4.5, "~", GPAcheck
courses::Vector{String}, "taken-courses"
professors::UN{DataFrame} = nothing, "table"
id::Int64, att"~"
comment::UN{String} = nothing, txt"end"
end
@aml mutable struct University doc"university"
name, att"university-name"
people::Vector{Person}, "person"
end
# Value Checking Functions
GPAcheck(x) = x <= 4.5 && x >= 0
function check_course(age, field, GPA, courses, professors, id, comment)
if field == "Mechanical Engineering"
relevant = ["Artificial Intelligence", "Robotics", "Machine Design"]
elseif field == "Computer Engineering"
relevant = ["Julia", "Algorithms"]
else
error("study field is not known")
end
return any(in.(courses, Ref(relevant)))
end
After we defined the structs, we can create instances of them by passing our data to the fields:
P1 = Person(age=24, field="Mechanical Engineering", courses = ["Artificial Intelligence", "Robotics"], id = 1, comment = "He is a genius")
P2 = Person(age=18, field="Computer Engineering", GPA=4, courses=["Julia"], id = 2)
U = University(name="Julia University", people=[P1, P2])
U.people[2].GPA=4.2 # mutability support after Doc creation
# An example that doesn't meet the criteria function for GPA because GPA is more than 4.5
P3 = Person(age=99, field="Macro Wizard", GPA=10, courses=["Julia Magic"], id = 3)
julia>
GPA doesn't meet criteria function
julia> pprint(P1) # or print(P1.aml)
<person id="1">
<age>24</age>
<study-field>Mechanical Engineering</study-field>
<GPA>4.5</GPA>
<taken-courses>Artificial Intelligence</taken-courses>
<taken-courses>Robotics</taken-courses>
He is a genius
</person>
julia> pprint(U) # or print(U.aml)
<?xml version="1.0" encoding="UTF-8"?>
<university university-name="Julia University">
<person id="1">
<age>24</age>
<study-field>Mechanical Engineering</study-field>
<GPA>4.5</GPA>
<taken-courses>Artificial Intelligence</taken-courses>
<taken-courses>Robotics</taken-courses>
He is a genius
</person>
<person id="2">
<age>18</age>
<study-field>Computer Engineering</study-field>
<GPA>4.2</GPA>
<taken-courses>Julia</taken-courses>
</person>
</university>
P3 with Tables.jl type:
Profs1 = DataFrame(course = ["Artificial Intelligence", "Robotics"], professor = ["Prof. A", "Prof. B"] )
P3 = Person(age=24, field="Mechanical Engineering", courses = ["Artificial Intelligence", "Robotics"], professors= Profs1, id = 1)
julia> pprint(P3)
<person id="1">
<age>24</age>
<study-field>Mechanical Engineering</study-field>
<GPA>4.5</GPA>
<taken-courses>Artificial Intelligence</taken-courses>
<taken-courses>Robotics</taken-courses>
<table>
<tr class="header">
<th style="text-align: right; ">course</th>
<th style="text-align: right; ">professor</th>
</tr>
<tr class="subheader headerLastRow">
<th style="text-align: right; ">String</th>
<th style="text-align: right; ">String</th>
</tr>
<tr>
<td style="text-align: right; ">Artificial Intelligence</td>
<td style="text-align: right; ">Prof. A</td>
</tr>
<tr>
<td style="text-align: right; ">Robotics</td>
<td style="text-align: right; ">Prof. B</td>
</tr>
</table>
</person>
After we defined the structs, we can automatically extract and store the data in their fields:
using AcuteML
xml = parsexml("""
<?xml version="1.0" encoding="UTF-8"?>
<university university-name="Julia University">
<person id="1">
<age>24</age>
<study-field>Mechanical Engineering</study-field>
<GPA>4.5</GPA>
<taken-courses>Artificial Intelligence</taken-courses>
<taken-courses>Robotics</taken-courses>
He is a genius
</person>
<person id="2">
<age>18</age>
<study-field>Computer Engineering</study-field>
<GPA>4.2</GPA>
<taken-courses>Julia</taken-courses>
</person>
</university>
""")
# extract University
U = University(xml) # StructName(xml) extracts the data and stores them in proper format
# Now you can access all of the data by calling the fieldnames
julia>U.name
"Julia University"
# extract Person
P1 = U.people[1]
julia>P1.age
24
julia>P1.field
Mechanical Engineering
julia>P1.GPA
4.5
julia>P1.courses
["Artificial Intelligence", "Robotics"]
julia>P1.id
1
julia> P1.comment
"He is a genius"
AcuteML also provides a templating engine if you want to use templates instead of creating the types.
This method only uses functions that return string. You can build your desired string and call the function for rendering.
## create person function to store out html template
newTemplate("person", :function)
function person(;id, age, field, GPA, courses)
# Build the taken courses section
loopOut=""
for course in courses
loopOut = loopOut * """ <taken-courses>$(course)</taken-courses> """
end
# Append all the sections and variables together
out = """
<person id=$(id)>
<age>$(age)</age>
<study-field>$(field)</study-field>
<GPA>$(GPA)</GPA>
$loopOut
</person>
"""
return out
end
# Call the function for rendering
out = person(
id = "1",
age = "24",
field = "Mechanical Engineering",
GPA = "4.5",
courses = ["Artificial Intelligence", "Robotics"]
)
print(out)
# you can also write the output to a file:
Base.write(filePath, out)
You can render variables into html/xml files. However, you can't have multiline control flow Julia code in this method.
# you can create a file and edit the file directly by using
newTemplate("person")
# Add the following html code to the generated html file
#=
<person id=$(id)>
<age>$(age)</age>
<study-field>$(field)</study-field>
<GPA>$(GPA)</GPA>
<taken-courses>$(courses[1])</taken-courses>
<taken-courses>$(courses[2])</taken-courses>
</person>
=#
# Specify the template (or its path), and also the variables for rendering
out =render2file("person", false,
id = 1,
age = 24,
field = "Mechanical Engineering",
GPA = 4.5,
courses = ["Artificial Intelligence", "Robotics"])
# you pass `true` as the 2nd argument to overwrite person.html statically.