Merge pull request #608 from triska/xml
ADDED: load_xml/3 to load XML files from streams, files and strings.
This commit is contained in:
@@ -45,3 +45,4 @@ openssl = { version = "0.10.29", features = ["vendored"] }
|
|||||||
native-tls = "0.2.4"
|
native-tls = "0.2.4"
|
||||||
chrono = "0.4.11"
|
chrono = "0.4.11"
|
||||||
select = "0.4.3"
|
select = "0.4.3"
|
||||||
|
roxmltree = "0.11.0"
|
||||||
|
|||||||
@@ -425,8 +425,9 @@ The modules that ship with Scryer Prolog are also called
|
|||||||
* [`http/http_open`](src/lib/http/http_open.pl) Open a stream to
|
* [`http/http_open`](src/lib/http/http_open.pl) Open a stream to
|
||||||
read answers from web servers. HTTPS is also supported.
|
read answers from web servers. HTTPS is also supported.
|
||||||
* [`sgml`](src/lib/sgml.pl)
|
* [`sgml`](src/lib/sgml.pl)
|
||||||
`load_html/3` represents HTML documents as Prolog terms
|
`load_html/3` and `load_xml/3` represent HTML and XML documents
|
||||||
for convenient and efficient reasoning. See also `library(xpath)`.
|
as Prolog terms for convenient and efficient reasoning. Use
|
||||||
|
`library(xpath)` to extract information from parsed documents.
|
||||||
* [`xpath`](src/lib/xpath.pl)
|
* [`xpath`](src/lib/xpath.pl)
|
||||||
The predicate `xpath/3` is used for convenient reasoning about
|
The predicate `xpath/3` is used for convenient reasoning about
|
||||||
HTML and XML documents, inspired by the XPath language. This
|
HTML and XML documents, inspired by the XPath language. This
|
||||||
|
|||||||
@@ -299,7 +299,8 @@ pub enum SystemClauseType {
|
|||||||
Ed25519Verify,
|
Ed25519Verify,
|
||||||
Ed25519NewKeyPair,
|
Ed25519NewKeyPair,
|
||||||
Ed25519KeyPairPublicKey,
|
Ed25519KeyPairPublicKey,
|
||||||
LoadHTML
|
LoadHTML,
|
||||||
|
LoadXML,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemClauseType {
|
impl SystemClauseType {
|
||||||
@@ -498,6 +499,7 @@ impl SystemClauseType {
|
|||||||
&SystemClauseType::Ed25519NewKeyPair => clause_name!("$ed25519_new_keypair"),
|
&SystemClauseType::Ed25519NewKeyPair => clause_name!("$ed25519_new_keypair"),
|
||||||
&SystemClauseType::Ed25519KeyPairPublicKey => clause_name!("$ed25519_keypair_public_key"),
|
&SystemClauseType::Ed25519KeyPairPublicKey => clause_name!("$ed25519_keypair_public_key"),
|
||||||
&SystemClauseType::LoadHTML => clause_name!("$load_html"),
|
&SystemClauseType::LoadHTML => clause_name!("$load_html"),
|
||||||
|
&SystemClauseType::LoadXML => clause_name!("$load_xml"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,6 +678,7 @@ impl SystemClauseType {
|
|||||||
("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair),
|
("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair),
|
||||||
("$ed25519_keypair_public_key", 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey),
|
("$ed25519_keypair_public_key", 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey),
|
||||||
("$load_html", 3) => Some(SystemClauseType::LoadHTML),
|
("$load_html", 3) => Some(SystemClauseType::LoadHTML),
|
||||||
|
("$load_xml", 3) => Some(SystemClauseType::LoadXML),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Predicates for parsing markup documents.
|
Predicates for parsing HTML and XML documents.
|
||||||
Written June 2020 by Markus Triska (triska@metalevel.at)
|
Written June 2020 by Markus Triska (triska@metalevel.at)
|
||||||
Part of Scryer Prolog.
|
Part of Scryer Prolog.
|
||||||
|
|
||||||
Currently, only a single predicate is provided:
|
Currently, two predicates are provided:
|
||||||
|
|
||||||
load_html(+In, -Es, +Options)
|
- load_html(+Source, -Es, +Options)
|
||||||
=============================
|
- load_xml(+Source, -Es, +Options)
|
||||||
|
|
||||||
In must be a stream, specified as stream(S), and Es is unified with
|
These predicates parse HTML and XML documents, respectively.
|
||||||
a list of elements of the form:
|
|
||||||
|
Source must be a stream, specified as stream(S), or a file,
|
||||||
|
specified as file(Name), where Name is a list of characters, or a
|
||||||
|
list of characters with the document contents.
|
||||||
|
|
||||||
|
Es is unified with the abstract syntax tree of the parsed document,
|
||||||
|
represented as a list of elements where each is of the form:
|
||||||
|
|
||||||
* a list of characters, representing text
|
* a list of characters, representing text
|
||||||
|
|
||||||
* element(Name, Attrs, Children)
|
* element(Name, Attrs, Children)
|
||||||
- Name is the name of the tag
|
- Name is the name of the tag
|
||||||
- Attrs is a list of Key=Value pairs:
|
- Attrs is a list of Key=Value pairs:
|
||||||
@@ -22,21 +27,62 @@
|
|||||||
Currently, Options are ignored. In the future, more options may be
|
Currently, Options are ignored. In the future, more options may be
|
||||||
provided to control parsing.
|
provided to control parsing.
|
||||||
|
|
||||||
Use http_open/3 from library(http/http_open) to read answers from
|
Example:
|
||||||
web servers via streams.
|
|
||||||
|
?- load_html("<html><head><title>Hello!</title></head></html>", Es, []).
|
||||||
|
|
||||||
|
Yielding:
|
||||||
|
|
||||||
|
Es = [element(html,[],
|
||||||
|
[element(head,[],
|
||||||
|
[element(title,[],
|
||||||
|
["Hello!"])]),
|
||||||
|
element(body,[],[])])].
|
||||||
|
|
||||||
library(xpath) provides convenient reasoning about parsed documents.
|
library(xpath) provides convenient reasoning about parsed documents.
|
||||||
|
For example, to fetch the title of the document above, we can use:
|
||||||
|
|
||||||
|
?- load_html("<html><head><title>Hello!</title></head></html>", Es, []),
|
||||||
|
xpath(Es, //title(text), T).
|
||||||
|
|
||||||
|
Yielding T = "Hello!".
|
||||||
|
|
||||||
|
Use http_open/3 from library(http/http_open) to read answers from
|
||||||
|
web servers via streams.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
:- module(sgml, [load_html/3]).
|
:- module(sgml, [load_html/3,
|
||||||
|
load_xml/3]).
|
||||||
|
|
||||||
:- use_module(library(iso_ext)).
|
:- use_module(library(iso_ext)).
|
||||||
:- use_module(library(error)).
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(pio)).
|
||||||
|
|
||||||
load_html(stream(Stream), [E], Options) :-
|
load_html(Source, Es, Options) :-
|
||||||
|
load_structure_(Source, Es, Options, html).
|
||||||
|
load_xml(Source, Es, Options) :-
|
||||||
|
load_structure_(Source, Es, Options, xml).
|
||||||
|
|
||||||
|
list([]) --> [].
|
||||||
|
list([L|Ls]) --> [L], list(Ls).
|
||||||
|
|
||||||
|
load_structure_([], [], _, _).
|
||||||
|
load_structure_([C|Cs], [E], Options, What) :-
|
||||||
|
load_(What, [C|Cs], E, Options).
|
||||||
|
load_structure_(file(Fs), [E], Options, What) :-
|
||||||
|
must_be(list, Options),
|
||||||
|
must_be(list, Fs),
|
||||||
|
atom_chars(File, Fs),
|
||||||
|
once(phrase_from_file(list(Cs), File)),
|
||||||
|
load_(What, Cs, E, Options).
|
||||||
|
load_structure_(stream(Stream), [E], Options, What) :-
|
||||||
must_be(list, Options),
|
must_be(list, Options),
|
||||||
read_to_end(Stream, Cs),
|
read_to_end(Stream, Cs),
|
||||||
'$load_html'(Cs, E, []).
|
load_(What, Cs, E, Options).
|
||||||
|
|
||||||
|
load_(html, Cs, E, Options) :- '$load_html'(Cs, E, Options).
|
||||||
|
load_(xml, Cs, E, Options) :- '$load_xml'(Cs, E, Options).
|
||||||
|
|
||||||
read_to_end(Stream, Cs) :-
|
read_to_end(Stream, Cs) :-
|
||||||
'$get_n_chars'(Stream, 4096, Cs0),
|
'$get_n_chars'(Stream, 4096, Cs0),
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
:- use_module(library(http/http_open)).
|
:- use_module(library(http/http_open)).
|
||||||
:- use_module(library(sgml)).
|
:- use_module(library(sgml)).
|
||||||
:- use_module(library(format)).
|
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
:- use_module(library(xpath)).
|
:- use_module(library(xpath)).
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ use crate::openssl::nid::Nid;
|
|||||||
use crate::native_tls::TlsConnector;
|
use crate::native_tls::TlsConnector;
|
||||||
|
|
||||||
extern crate select;
|
extern crate select;
|
||||||
|
use roxmltree;
|
||||||
|
|
||||||
pub fn get_key() -> KeyEvent {
|
pub fn get_key() -> KeyEvent {
|
||||||
let key;
|
let key;
|
||||||
@@ -5568,11 +5569,71 @@ impl MachineState {
|
|||||||
|
|
||||||
self.unify(self[temp_v!(2)], result);
|
self.unify(self[temp_v!(2)], result);
|
||||||
}
|
}
|
||||||
|
&SystemClauseType::LoadXML => {
|
||||||
|
let string = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
|
||||||
|
match roxmltree::Document::parse(&string) {
|
||||||
|
Ok(doc) => { let result = self.xml_node_to_term(indices, doc.root_element());
|
||||||
|
self.unify(self[temp_v!(2)], result);
|
||||||
|
}
|
||||||
|
_ => { self.fail = true;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return_from_clause!(self.last_call, self)
|
return_from_clause!(self.last_call, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super)
|
||||||
|
fn xml_node_to_term(
|
||||||
|
&mut self,
|
||||||
|
indices: &mut IndexStore,
|
||||||
|
node: roxmltree::Node,
|
||||||
|
) -> Addr {
|
||||||
|
if node.has_children() {
|
||||||
|
let mut avec = Vec::new();
|
||||||
|
for attr in node.attributes() {
|
||||||
|
let chars = clause_name!(String::from(attr.name()), indices.atom_tbl);
|
||||||
|
let name = self.heap.to_unifiable(
|
||||||
|
HeapCellValue::Atom(chars, None)
|
||||||
|
);
|
||||||
|
|
||||||
|
let value = self.heap.put_complete_string(&attr.value());
|
||||||
|
|
||||||
|
avec.push(HeapCellValue::Addr(Addr::HeapCell(self.heap.h())));
|
||||||
|
|
||||||
|
self.heap.push(HeapCellValue::NamedStr(2, clause_name!("="), None));
|
||||||
|
self.heap.push(HeapCellValue::Addr(name));
|
||||||
|
self.heap.push(HeapCellValue::Addr(value));
|
||||||
|
}
|
||||||
|
let attrs = Addr::HeapCell(self.heap.to_list(avec.into_iter()));
|
||||||
|
|
||||||
|
let mut cvec = Vec::new();
|
||||||
|
for child in node.children() {
|
||||||
|
cvec.push(self.xml_node_to_term(indices, child));
|
||||||
|
}
|
||||||
|
let children = Addr::HeapCell(self.heap.to_list(cvec.into_iter()));
|
||||||
|
|
||||||
|
let chars = clause_name!(String::from(node.tag_name().name()), indices.atom_tbl);
|
||||||
|
let tag = self.heap.to_unifiable(
|
||||||
|
HeapCellValue::Atom(chars, None)
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = Addr::HeapCell(self.heap.h());
|
||||||
|
|
||||||
|
self.heap.push(HeapCellValue::NamedStr(3, clause_name!("element"), None));
|
||||||
|
self.heap.push(HeapCellValue::Addr(tag));
|
||||||
|
self.heap.push(HeapCellValue::Addr(attrs));
|
||||||
|
self.heap.push(HeapCellValue::Addr(children));
|
||||||
|
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
let string = String::from(node.text().unwrap());
|
||||||
|
self.heap.put_complete_string(&string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super)
|
pub(super)
|
||||||
fn html_node_to_term(
|
fn html_node_to_term(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
Reference in New Issue
Block a user