This commit is contained in:
Mariusz Mączkowski 2018-11-19 18:41:53 +01:00
parent 9eb3874241
commit bd18f6c2fb
2 changed files with 505 additions and 0 deletions

489
zsyi-l4/dom.js Normal file
View File

@ -0,0 +1,489 @@
// Copyright 2018 Johannes Wilm
// Copyright 2005 Google Inc.
// All Rights Reserved
//
// Original author: Steffen Meschkat <mesch@google.com>
//
// An XML parse and a minimal DOM implementation that just supports
// the subset of the W3C DOM that is used in the XSLT implementation.
import he from "he"
import {
domSetAttribute,
domAppendChild,
domCreateTextNode,
domCreateElement,
domCreateCDATASection,
domCreateComment
} from "./util.js"
import {
XML10_VERSION_INFO,
XML10_NAME,
XML10_ATTRIBUTE,
XML11_VERSION_INFO,
XML11_NAME,
XML11_ATTRIBUTE
} from "./xmltoken.js"
const XML10_TAGNAME_REGEXP = new RegExp(`^(${XML10_NAME})`);
const XML10_ATTRIBUTE_REGEXP = new RegExp(XML10_ATTRIBUTE, 'g');
const XML11_TAGNAME_REGEXP = new RegExp(`^(${XML11_NAME})`);
const XML11_ATTRIBUTE_REGEXP = new RegExp(XML11_ATTRIBUTE, 'g');
// Parses the given XML string with our custom, JavaScript XML parser. Written
// by Steffen Meschkat (mesch@google.com).
export function xmlParse(xml) {
const regex_empty = /\/$/;
let regex_tagname;
let regex_attribute;
if (xml.match(/^<\?xml/)) {
// When an XML document begins with an XML declaration
// VersionInfo must appear.
if (xml.search(new RegExp(XML10_VERSION_INFO)) == 5) {
regex_tagname = XML10_TAGNAME_REGEXP;
regex_attribute = XML10_ATTRIBUTE_REGEXP;
} else if (xml.search(new RegExp(XML11_VERSION_INFO)) == 5) {
regex_tagname = XML11_TAGNAME_REGEXP;
regex_attribute = XML11_ATTRIBUTE_REGEXP;
} else {
// VersionInfo is missing, or unknown version number.
// TODO : Fallback to XML 1.0 or XML 1.1, or just return null?
throw('VersionInfo is missing, or unknown version number.');
}
} else {
// When an XML declaration is missing it's an XML 1.0 document.
regex_tagname = XML10_TAGNAME_REGEXP;
regex_attribute = XML10_ATTRIBUTE_REGEXP;
}
const xmldoc = new XDocument();
const root = xmldoc;
const stack = []; // MM - stack is used to hold current parser position
let parent = root;
stack.push(parent); // MM - put document root on stack
let tag = false, // MM - if parser visited tag already; is inside tag
quotes = false, // MM - if parser visited quotes already; is inside quoted text
doublequotes = false, // MM - if parser visited doublequotes already; is inside doublequoted text
start = 0;
// MM - This loop iterates over whole document char by char
for (let i = 0; i < xml.length; ++i) {
let char = xml.charAt(i);
if (tag && char === "'") { // MM - enter to quoted text or exit from it
quotes = !quotes;
} else if (tag && char === "\"") { // MM - enter to doublequoted text or exit from it
doublequotes = !doublequotes;
} else if (tag && char === ">" && !quotes && !doublequotes) { // MM - enter valid tag or exit from it
let text = xml.slice(start, i);
if (text.charAt(0) == '/') { // MM - empty tag, remove from stack
stack.pop();
parent = stack[stack.length - 1];
} else if (text.charAt(0) == '?') {
// Ignore XML declaration and processing instructions
} else if (text.charAt(0) == '!') {
// Ignore malformed notation and comments
} else {
const empty = text.match(regex_empty);
const tagname = regex_tagname.exec(text)[1];
let node = domCreateElement(xmldoc, tagname);
let att;
while ((att = regex_attribute.exec(text))) {// MM - set up attributes to element
const val = he.decode(att[5] || att[7] || '');
domSetAttribute(node, att[1], val);
}
domAppendChild(parent, node);
if (!empty) {
parent = node;
stack.push(node);
}
}
start = i + 1;
tag = false;
quotes = false;
doublequotes = false;
} else if (!tag && char === "<") {// MM - open tag char encountered
let text = xml.slice(start, i)
if (text && parent != root) {
domAppendChild(parent, domCreateTextNode(xmldoc, text));
}
if (xml.slice(i+1,i+4)==="!--") { // MM - comment in xml code, create comment in dom and skip to next char
let endTagIndex = xml.slice(i+4).indexOf('-->');
if (endTagIndex) {
let node = domCreateComment(xmldoc, xml.slice(i+4, i+endTagIndex+4));
domAppendChild(parent, node);
i += endTagIndex+6;
}
} else if (xml.slice(i + 1, i + 9) === "![CDATA[") { // MM - CDATA section in xml code, create CDATA in dom and skip to next char
let endTagIndex = xml.slice(i+9).indexOf(']]>');
if (endTagIndex) {
let node = domCreateCDATASection(xmldoc, xml.slice(i+9, i+endTagIndex+9));
domAppendChild(parent, node);
i += endTagIndex+11;
}
} else {
tag = true; // MM - not encountered comment or CDATA section, mark that parser is in tag
}
start = i + 1;
}
}
return root;
}
// Based on <http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/
// core.html#ID-1950641247>
export const DOM_ELEMENT_NODE = 1;
export const DOM_ATTRIBUTE_NODE = 2;
export const DOM_TEXT_NODE = 3;
export const DOM_CDATA_SECTION_NODE = 4;
// const DOM_ENTITY_REFERENCE_NODE = 5;
// const DOM_ENTITY_NODE = 6;
export const DOM_PROCESSING_INSTRUCTION_NODE = 7;
export const DOM_COMMENT_NODE = 8;
export const DOM_DOCUMENT_NODE = 9;
// const DOM_DOCUMENT_TYPE_NODE = 10;
export const DOM_DOCUMENT_FRAGMENT_NODE = 11;
// const DOM_NOTATION_NODE = 12;
// Traverses the element nodes in the DOM section underneath the given
// node and invokes the given callbacks as methods on every element
// node encountered. Function opt_pre is invoked before a node's
// children are traversed; opt_post is invoked after they are
// traversed. Traversal will not be continued if a callback function
// returns boolean false. NOTE(mesch): copied from
// <//google3/maps/webmaps/javascript/dom.js>.
function domTraverseElements(node, opt_pre, opt_post) {
let ret;
if (opt_pre) {
ret = opt_pre.call(null, node);
if (typeof ret == 'boolean' && !ret) {
return false;
}
}
for (let c = node.firstChild; c; c = c.nextSibling) {
if (c.nodeType == DOM_ELEMENT_NODE) {
ret = domTraverseElements.call(this, c, opt_pre, opt_post);
if (typeof ret == 'boolean' && !ret) {
return false;
}
}
}
if (opt_post) {
ret = opt_post.call(null, node);
if (typeof ret == 'boolean' && !ret) {
return false;
}
}
}
let _unusedXNodes = [];
// Our W3C DOM Node implementation. Note we call it XNode because we
// can't define the identifier Node. We do this mostly for Opera,
// where we can't reuse the HTML DOM for parsing our own XML, and for
// Safari, where it is too expensive to have the template processor
// operate on native DOM nodes.
export class XNode {
constructor(type, name, opt_value, opt_owner) {
this.attributes = [];
this.childNodes = [];
this.init(type, name, opt_value, opt_owner);
}
init(type, name, value, owner) {
this.nodeType = type - 0;
this.nodeName = `${name}`;
this.nodeValue = `${value}`;
this.ownerDocument = owner;
this.firstChild = null;
this.lastChild = null;
this.nextSibling = null;
this.previousSibling = null;
this.parentNode = null;
}
static recycle(node) {
if (!node) {
return;
}
if (node.constructor == XDocument) {
this.recycle(node.documentElement);
return;
}
if (node.constructor != this) {
return;
}
_unusedXNodes.push(node);
for (let a = 0; a < node.attributes.length; ++a) {
this.recycle(node.attributes[a]);
}
for (let c = 0; c < node.childNodes.length; ++c) {
this.recycle(node.childNodes[c]);
}
node.attributes.length = 0;
node.childNodes.length = 0;
node.init.call(0, '', '', null);
}
create(type, name, value, owner) {
if (_unusedXNodes.length > 0) {
const node = _unusedXNodes.pop();
node.init(type, name, value, owner);
return node;
} else {
return new XNode(type, name, value, owner);
}
}
appendChild(node) {
// firstChild
if (this.childNodes.length == 0) {
this.firstChild = node;
}
// previousSibling
node.previousSibling = this.lastChild;
// nextSibling
node.nextSibling = null;
if (this.lastChild) {
this.lastChild.nextSibling = node;
}
// parentNode
node.parentNode = this;
// lastChild
this.lastChild = node;
// childNodes
this.childNodes.push(node);
}
replaceChild(newNode, oldNode) {
if (oldNode == newNode) {
return;
}
for (let i = 0; i < this.childNodes.length; ++i) {
if (this.childNodes[i] == oldNode) {
this.childNodes[i] = newNode;
let p = oldNode.parentNode;
oldNode.parentNode = null;
newNode.parentNode = p;
p = oldNode.previousSibling;
oldNode.previousSibling = null;
newNode.previousSibling = p;
if (newNode.previousSibling) {
newNode.previousSibling.nextSibling = newNode;
}
p = oldNode.nextSibling;
oldNode.nextSibling = null;
newNode.nextSibling = p;
if (newNode.nextSibling) {
newNode.nextSibling.previousSibling = newNode;
}
if (this.firstChild == oldNode) {
this.firstChild = newNode;
}
if (this.lastChild == oldNode) {
this.lastChild = newNode;
}
break;
}
}
}
insertBefore(newNode, oldNode) {
if (oldNode == newNode) {
return;
}
if (oldNode.parentNode != this) {
return;
}
if (newNode.parentNode) {
newNode.parentNode.removeChild(newNode);
}
const newChildren = [];
for (const c of this.childNodes) {
if (c == oldNode) {
newChildren.push(newNode);
newNode.parentNode = this;
newNode.previousSibling = oldNode.previousSibling;
oldNode.previousSibling = newNode;
if (newNode.previousSibling) {
newNode.previousSibling.nextSibling = newNode;
}
newNode.nextSibling = oldNode;
if (this.firstChild == oldNode) {
this.firstChild = newNode;
}
}
newChildren.push(c);
}
this.childNodes = newChildren;
}
removeChild(node) {
const newChildren = [];
for (const c of this.childNodes) {
if (c != node) {
newChildren.push(c);
} else {
if (c.previousSibling) {
c.previousSibling.nextSibling = c.nextSibling;
}
if (c.nextSibling) {
c.nextSibling.previousSibling = c.previousSibling;
}
if (this.firstChild == c) {
this.firstChild = c.nextSibling;
}
if (this.lastChild == c) {
this.lastChild = c.previousSibling;
}
}
}
this.childNodes = newChildren;
}
hasAttributes() {
return this.attributes.length > 0;
}
setAttribute(name, value) {
for (let i = 0; i < this.attributes.length; ++i) {
if (this.attributes[i].nodeName == name) {
this.attributes[i].nodeValue = `${value}`;
return;
}
}
this.attributes.push(this.create(DOM_ATTRIBUTE_NODE, name, value, this));
}
getAttribute(name) {
for (let i = 0; i < this.attributes.length; ++i) {
if (this.attributes[i].nodeName == name) {
return this.attributes[i].nodeValue;
}
}
return null;
}
removeAttribute(name) {
const a = [];
for (let i = 0; i < this.attributes.length; ++i) {
if (this.attributes[i].nodeName != name) {
a.push(this.attributes[i]);
}
}
this.attributes = a;
}
getElementsByTagName(name) {
const ret = [];
const self = this;
if ("*" == name) {
domTraverseElements(this, node => {
if (self == node) return;
ret.push(node);
}, null);
} else {
domTraverseElements(this, node => {
if (self == node) return;
if (node.nodeName == name) {
ret.push(node);
}
}, null);
}
return ret;
}
getElementById(id) {
let ret = null;
domTraverseElements(this, node => {
if (node.getAttribute('id') == id) {
ret = node;
return false;
}
}, null);
return ret;
}
}
export class XDocument extends XNode {
constructor() {
// NOTE(mesch): According to the DOM Spec, ownerDocument of a
// document node is null.
super(DOM_DOCUMENT_NODE, '#document', null, null);
this.documentElement = null;
}
clear() {
this.recycle(this.documentElement);
this.documentElement = null;
}
appendChild(node) {
super.appendChild(node);
this.documentElement = this.childNodes[0];
}
createElement(name) {
return super.create(DOM_ELEMENT_NODE, name, null, this);
}
createDocumentFragment() {
return super.create(DOM_DOCUMENT_FRAGMENT_NODE, '#document-fragment',
null, this);
}
createTextNode(value) {
return super.create(DOM_TEXT_NODE, '#text', value, this);
}
createAttribute(name) {
return super.create(DOM_ATTRIBUTE_NODE, name, null, this);
}
createComment(data) {
return super.create(DOM_COMMENT_NODE, '#comment', data, this);
}
createCDATASection(data) {
return super.create(DOM_CDATA_SECTION_NODE, '#cdata-section', data, this);
}
}
//XDocument.prototype = new XNode(DOM_DOCUMENT_NODE, '#document');

16
zsyi-l4/raport.md Normal file
View File

@ -0,0 +1,16 @@
Całość została wykonana przez jedna osobę: Mariusz Mączkowski, numer indeksu: 396378
### Zadanie 1
Wykorzystałem odbyc kod udostępniony na platformie GitHub. Analizowałem go w celach wykrycia błędu, który znalazłem i poprawiłem. Kod został zaczerpnięty z repozytorium dostępnym pod poniższym linkiem.
https://github.com/fiduswriter/xslt-processor/blob/master/src/dom.js
W pliku `dom.js` zawarłem dodatkowe komentarze opisujące działanie kodu. Przedmiotem analizy była dokładnie funkcja `xmlParse`, która służy do parsowania zawartości pliku xml i budowania struktury typu DOM (Document object model).
### Zadanie 2
W grze bierze udział gracz, a jego przeciwnikiem jest komputer. Na stół trafia określona liczba ciastek. Gracz i komputer naprzemiennie mogą zabierać i zjadać od 1 do 3 ciastek. Przegrywa ten, kto zje ostatnie ciastko. Jesli komputer zostawi tylko jedno ciastko to też przegrywa bo nie zostawił innej możliwości graczowi niż zjedzenie pozostałego ciastka.
### Zadanie 3
International Obfuscated C Code Contest - "Konkurs na najbardziej zaciemniony kod" - skierowany do języka C. Założeniami było napisanie totalnie niezrozumiałego kodu, który będzie w jednocześnie wyglądał efektownie, a zarazem wykorzysta specyfikę kompilatorów. Celem było unaocznienie istoty pisania kodu zrozumiałego również dla programistów (nie tylko dla komputera), a przy okazji ujawnienie mało znanych konstrukcji języka i możliwości kompilatora.
Zaskakującym elementem jest to, że przedstawiane w konkursie programy rzeczywiście działają.