Main Macro and IO

AcuteML.@aml — Macro

@aml

Type Definition

Use @aml macro to define a Julia type, and then the package automatically creates a xml or html associated with the defined type. The general syntax would look like this:

@aml mutable struct mybody "body"
    myh1, "h1"
    p::Vector{String}, "~"
end

You can also define the structs as parametric:

@aml mutable struct MyGeneralXML{T} "my-general-xml"
    myfield::T, "~"
end
pxml_string = MyGeneralXML{String}(myfield = "a")
pprint(pxml_string)
pxml_vector = MyGeneralXML{Vector{String}}(myfield = ["b","c"])
pprint(pxml_vector)

Now, we go into the details:

Document Definition

  • Use doc literal before the root name to define a HTML or XML document. For HTML documents root should always be "html".
@aml mutable struct Doc doc"xml_root"
# add fields (elements) here
end
@aml mutable struct Web doc"html"
# add fields (elements) here
end

Nodes (Elements) Definition

  • Specify the html/xml name of struct as a string after the struct name (after a space)
@aml mutable struct Person "person"
# add fields (elements) here
end
  • If the html/xml name is the same as struct name, you can use "~" instead
@aml mutable struct person "~"
# add fields (elements) here
end

Fields Names

  • Sepecify the html/xml field name as a string in front of the field after ,
field, "study-field"
  • If the html/xml name is the same as variable name, you can use "~" instead
age::UInt, "~"
Warning

The field names of a struct should not be the same as other defined types. This error happens when you use the same name of a type for a field name. For example, the follwing is an error:

@aml struct person "~"
    name, "~"
end
@aml struct myxml doc"~"
    person::person, "~"
end

Another example of this error:

@aml struct myxml doc"~"
    Int, "myint"
end

However, you can choose any xml/html name. The xml/html name of the fields isn't related to the types defined in Julia. So the following is a valid syntax:

@aml struct myxml doc"~"
    myint, "Int"
end

Attributes

  • If the value is going to be an attribute put att before its name
id::Int64, att"~"

Default Value

  • You can specify the default value for an argument by using = defVal syntax
GPA::Float64 = 4.5, "~"

Value Types

You can use Julia types or defined types for values. see and Supported Value Types Custom Value Types for more information.

  • If you don't specify the type of a variable, it is considered to be string for aml manipulations:
field, "study-field"

However, for a high performance code specify String type (field::String, "study-field")

  • For already @aml defined types, name should be the same as the defined type root name
university::University, "university"
  • Table types are supported through PrettyTables.jl.

Value Checking

You can define any restriction for values using functions.

  • To define any restrictions for the values of one field, define a function that checks a criteria for the field value and returns Bool, and put its name after a , after the field name:
GPA::Float64, "~", GPAcheck
  • To define any restrictions for multiple values of a struct, define a function that gets all the variables and checks a criteria and returns Bool, and put its name after a , after the struct name:
@aml mutable struct Person "person", check_course
# ...
end

Refer to https://aminya.github.io/AcuteML.jl/dev/valueChecking/ for some of these functions examples.

Optional Fields

  • If a field is optional, don't forget to define its type as UN{} (Union with Nothing), and set the default value as nothing.
residence::UN{String}=nothing, "residence-stay" # optional with nothing as default value
funds::UN{String}, "financial-funds"   # optional, but you should pass nothing manually in construction

Text Nodes

If the value is going to be in a Text node:

  • use txt"index" for non-vector field type, which index is an Integer that shows the positon of text node. If you give txt"" it considers it like txt"1".
textnode_single:String, txt"2"
  • use txt"indices" for vector field type, which indices is an array index that shows the positons of the text nodes. If you give txt"" it considers it like txt"[:]"
textnode_vector::Vector{String}, txt"[2:3]"

Note that the vector Text nodes should only be used as the last field of a struct (because possible positons for text node should be known). Alternatively, you can make non-vector separate fields with correct position in the struct definition.

Empty Elements (Self-Closing) Definition

  • Use empty"name" to define an empty (self-closing) element (e.g. <rest />)
@aml struct rest empty"~"
end

Example - Simple

using AcuteML

@aml mutable struct body "~"
    h1, "~"
    p::Vector{String}, "~"
end

@aml mutable struct html doc"html"
    body::body, "~"
end

b = body(h1 = "My heading", p = ["Paragraph1", "Paragraph2"])
d = html(body = b)
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>

Example - Struct Definition

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

Example - Creator

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>

Example - Extractor

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.pprint — Method
pprint(x)
pprint(io, x)
pprint(filename, x)

Pretty prints the xml/html content of a aml type. Also, pretty prints a Node or Document type.

EzXML.parsehtml — Function
parsehtml(htmlstring)

Parse htmlstring and create an HTML document.

EzXML.parsexml — Function
parsexml(xmlstring)

Parse xmlstring and create an XML document.

EzXML.readhtml — Function
readhtml(filename)

Read filename and create an HTML document.

readhtml(input::IO)

Read input and create an HTML document.

EzXML.readxml — Function
readxml(filename)

Read filename and create an XML document.

readxml(input::IO)

Read input and create an XML document.

Templating

AcuteML.newTemplate — Function
newTemplate(name)

Create new destination html file as the template

newTemplate(name, :function)

Prints a function to be used as a template

Examples

# you can create a file and edit the file directly by using
newTemplate("person")

## create person function to store out html template
newTemplate("person", :function)
AcuteML.render2file — Function
render2file(destination, overwrite, var...)

render variables passed as an input to the destination file.

You should put var in the destination file/string so var is evaluated there. Pass the variables as keyword arguments with the same name you used in the html string/file. Variables should be string,

If you want to statically overwrite the file pass true as the 2nd argument to the function. Useful if you don't want a dynamic website.

Examples

# 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 owerwrite person.html statically.

Macro Backend

XML/HTML utilities