commit 98c103924fd3dd5f666cafaf7193d639de6d444d
parent 97069af77a3d0be2e4390b0e4e8d90beeec99396
Author: Demonstrandum <moi@knutsen.co>
Date: Sat, 27 Jun 2020 11:26:42 +0100
Added XML converter.
Diffstat:
10 files changed, 258 insertions(+), 17 deletions(-)
diff --git a/samples/css-concept.sex b/samples/css-concept.sex
@@ -0,0 +1,26 @@
+;; Very generally:
+;a b c {
+; x: y;
+; z: w;
+;}
+
+;; Becomes:
+(a b c :x y
+ :z w)
+
+;; Take a proper example:
+
+;p #para-id {
+; width: calc(3em + 6px);
+; color: green;
+; margin: 3px, auto, 4px, 2px;
+; position: absolute;
+;}
+
+;; Becomes:
+(p #para-id
+ :width (calc (+ 3em 6px))
+ :color "green"
+ :margin (3px auto 4px 2px)
+ :position absolute)
+
diff --git a/samples/js-concept.sex b/samples/js-concept.sex
@@ -0,0 +1,57 @@
+
+;; Example JavaScript:
+
+;import * as BC from '../lib/BasicCanvas.js';
+;import {grid, ellipse} from '../lib/BasicShapes.js';
+;
+;use(BC);
+;
+;const sketch = canvas_id('sketch');
+;
+;sketch.dimensions(400, 400);
+;sketch.translate(200, 200);
+;sketch.scale(30, -30);
+;
+;sketch.fill = Color`transparent`;
+;sketch.stroke = Color`black`;
+;
+;const gaussian = (mean = 0, σ = 1) => {
+; const ϑ = Math.random() * Math.TAU;
+; const ρ = Math.sqrt(-2 * Math.log(1 - Math.random()));
+; const radius = σ * ρ;
+;
+; return Polar(radius, ϑ, P(mean, mean));
+;};
+;
+;const points = [];
+;const r = 0.4;
+
+
+(import * as BC from "../lib/BasicCanvas.js")
+(import { grid, ellipse } from "../lib/BasicCanvas.js")
+
+(use BC)
+
+(const sketch (canvas_id "sketch"))
+
+;; We have toistinguish operators from function calls.
+((. sketch dimensions) 400 400)
+(sketch.translate 200 200) ;; Alternative (won't work with spaces).
+((. sketch scale) 30 -30)
+
+(= (. sketch fill) (Color `transparent`))
+(= sketch.stroke (Color `black`))
+
+(const gaussian (=> (= mean 0) (= σ 1)
+ (const ϑ (* (. Math TAU) ((. Math random)))
+ (const ρ (Math.sqrt (* -2 (Math.log (- 1 (Math.random))))))
+ (const radius (* σ ρ))
+
+ (Polar radius ϑ (P mean mean)))))
+
+(const points [])
+(const r 0.4)
+
+
+
+
diff --git a/samples/xml-example-1.sex b/samples/xml-example-1.sex
@@ -1,7 +1,7 @@
(message :status urgernt
(to Tove)
(from Jani)
- (heading A \"reminder\" \(Again!\) for you)
- (body Don't forget me this weekend!))
+ (heading A reminder \(Again!\) for you)
+ (body Don't \"forget\" me this weekend!))
diff --git a/samples/xml-schema.sex b/samples/xml-schema.sex
@@ -0,0 +1,4 @@
+(?xml :version 1.0 :encoding ISO-8859-1)
+(xs:schema :xmlns:xs "http://www.w3.org/2001/XMLSchema")
+;; Symbols can have colones (:) inside them, just not at the start,
+;; because that's an attribute (they can be escaped with '\').
diff --git a/src/assemble/html.rs b/src/assemble/html.rs
@@ -19,8 +19,10 @@ pub const DEFAULT : &str =
"<!DOCTYPE html>\n\
<html>\n\
<head></head>\n\
- <body></body>\n\
- </html>";
+ <body>\n\
+ <!-- Generated by SEAM (empty file) -->\n\
+ </body>\n\
+ </html>\n";
impl Documentise for HTMLFormatter {
fn document(&self) -> String {
@@ -94,10 +96,9 @@ impl Documentise for HTMLFormatter {
}
// Populate.
- doc += "<!-- Generated from symbolic-expressions \
- into HTML. -->\n";
doc += &self.to_string();
-
+ doc += "<!-- Generated by SEAM, from symbolic-expressions \
+ into HTML. -->\n";
// Cloes all new tags.
if !body_tag {
doc += "</body>\n";
diff --git a/src/assemble/mod.rs b/src/assemble/mod.rs
@@ -2,4 +2,5 @@ pub trait Documentise {
fn document(&self) -> String;
}
+pub mod xml;
pub mod html;
diff --git a/src/assemble/xml.rs b/src/assemble/xml.rs
@@ -0,0 +1,146 @@
+//! Assembles an expanded tree into valid XML.
+use super::Documentise;
+use crate::parse::parser::{self, ParseNode, ParseTree};
+
+use std::fmt::{self, Display};
+
+#[derive(Debug, Clone)]
+pub struct XMLFormatter {
+ pub tree : ParseTree
+}
+
+impl XMLFormatter {
+ pub fn new(tree : ParseTree) -> Self {
+ Self { tree }
+ }
+
+ fn show_attribute(&self, attr : &parser::AttributeNode)
+ -> Result<String, fmt::Error> {
+ if let Some(symbol) = (*attr.node).atomic() {
+ Ok(format!("{}=\"{}\"", attr.keyword, symbol.value))
+ } else {
+ Err(fmt::Error)
+ }
+ }
+
+}
+
+pub const DEFAULT : &str =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
+
+impl Documentise for XMLFormatter {
+ fn document(&self) -> String {
+ let mut doc = String::new();
+ if self.tree.is_empty() {
+ return String::from(DEFAULT);
+ }
+ let stripped = parser::strip(&self.tree, true);
+ let current_node = stripped.get(0);
+
+ // Check if declaration exists.
+ let mut has_declaration = false;
+ if let Some(ParseNode::List(list)) = current_node.as_ref() {
+ if let Some(ParseNode::Symbol(declaration)) = list.get(0) {
+ if declaration.value.to_lowercase() == "?xml" {
+ has_declaration = true;
+ }
+ }
+ }
+
+ if !has_declaration {
+ doc += DEFAULT;
+ }
+
+ // Populate.
+ doc += &self.to_string();
+ doc += "<!-- Generated by SEAM, from symbolic-expressions \
+ into XML. -->\n";
+ doc
+ }
+}
+
+
+// TODO: Convert special characters to HTML compatible ones.
+// e.g.
+// < => <
+// > => >
+// & => &
+// " => "
+// ! => !
+// etc.
+
+/// Converting the tree to an HTML string.
+impl Display for XMLFormatter {
+ fn fmt(&self, f : &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut tree_iter = self.tree.iter().peekable();
+ while let Some(node) = tree_iter.next() {
+ match node {
+ ParseNode::Symbol(node)
+ | ParseNode::Number(node) => {
+ // If the node ahead is so-called "symbolic", we can
+ // infere there was a space between them.
+ write!(f, "{}", node.value)?;
+ if let Some(peek) = tree_iter.peek() {
+ if peek.symbolic().is_some() {
+ write!(f, " ")?
+ }
+ }
+ },
+ ParseNode::String(node) => write!(f, "{}", node.value)?,
+ ParseNode::List(list) => {
+ let head = list.first();
+ let mut tag = "";
+ if let Some(head_node) = head {
+ if let ParseNode::Symbol(head_symbol) = head_node {
+ tag = &head_symbol.value;
+ write!(f, "<{}", tag)?;
+ } else {
+ // Error, tags can only have symbol values.
+ }
+ } else {
+ // Error, empty tags not supported.
+ }
+
+ let mut rest = &list[1..];
+
+ // Declarations behave differently.
+ let front = tag.as_bytes()[0] as char;
+ if front == '!' || front == '?' {
+ while !rest.is_empty() {
+ if let ParseNode::List(_list) = &rest[0] {
+ // TODO: Throw error.
+ } else {
+ if let Some(node) = rest[0].symbolic() {
+ write!(f, "{}", node.value)?;
+ } else if let ParseNode::Attribute(a) = &rest[0] {
+ write!(f, " {}", self.show_attribute(a)?)?;
+ } else {
+ // Error.
+ }
+ }
+ rest = &rest[1..];
+ }
+ if front == '?' {
+ write!(f, " ?>")?;
+ } else {
+ write!(f, ">")?;
+ }
+ continue;
+ }
+
+ while let Some(ParseNode::Attribute(attr)) = rest.first() {
+ write!(f, " {}", self.show_attribute(&attr)?)?;
+ rest = &rest[1..];
+ }
+ write!(f, ">")?;
+
+ let xml_fmt = XMLFormatter::new(rest.to_owned());
+ write!(f, "{}", xml_fmt)?;
+ write!(f, "</{}>", tag)?;
+ },
+ _ => panic!("Uh {:?}", node),
+ }
+ }
+ write!(f, "")
+ }
+}
diff --git a/src/bin.rs b/src/bin.rs
@@ -14,7 +14,7 @@ fn argument_fatal(msg : &str) -> ! {
std::process::exit(1)
}
-const SUPPORTED_TARGETS : [&str; 1] = ["html"];
+const SUPPORTED_TARGETS : [&str; 2] = ["html", "xml"];
fn main() -> Result<(), Box<dyn Error>> {
let (major, minor, tiny) = seam::VERSION;
@@ -54,14 +54,20 @@ fn main() -> Result<(), Box<dyn Error>> {
eprintln!("{}", &tree
.iter().fold(String::new(),
|acc, s| acc + "\n" + &s.to_string()));
- if target == "html" {
- let fmt = seam::assemble::html::HTMLFormatter::new(tree);
- let result = fmt.document();
- println!("{}", result);
- }
+ let result = match target {
+ "html" => {
+ let fmt = seam::assemble::html::HTMLFormatter::new(tree);
+ fmt.document()
+ },
+ "xml" => {
+ let fmt = seam::assemble::xml::XMLFormatter::new(tree);
+ fmt.document()
+ },
+ _ => continue
+ };
+ print!("{}", result);
}
- eprintln!("All files read and converted.");
Ok(())
}
diff --git a/test.html b/test.html
@@ -1,4 +1,3 @@
-<!-- Generated from symbolic-expressions into HTML. -->
<!DOCTYPE html>
<html lang="en"><head><title>Example HTML Document</title></head>
<body><p id="hello">Hello, World!</p>
@@ -6,5 +5,6 @@
<h1>A (big) Header!</h1>
<p>Yet some more <span style="color: red">text</span> <3</p>
<p>Hello<span style="color: green">World</span>!</p>
-<img alt="Cat" src="https://static.insider.com/image/5d24d6b921a861093e71fef3.jpg" width="300"></img></body></html>
+<img alt="Cute cat" src="https://static.insider.com/image/5d24d6b921a861093e71fef3.jpg" width="300"></img></body></html>
+<!-- Generated by SEAM, from symbolic-expressions into HTML. -->
diff --git a/test.sex b/test.sex
@@ -10,7 +10,7 @@
(span :style "color: red" text) <3)
(p Hello(span :style "color: green" World)!)
(img
- :alt Cat
+ :alt "Cute cat"
:src "https://static.insider.com/image/5d24d6b921a861093e71fef3.jpg"
:width 300)))