Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Term memoization #272

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions src/blank-node.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
'use strict'
const ClassOrder = require('./class-order')
const Node = require('./node')
const Term = require('./term')

class BlankNode extends Term {
/** @private */
static normalizeID (id) {
if (typeof id === 'string' && id.includes('#')) {
// Is a URI with hash fragment
let fragments = id.split('#')
return fragments[fragments.length - 1]
}

return id
}

class BlankNode extends Node {
constructor (id) {
super()
this.termType = BlankNode.termType
Expand All @@ -12,13 +23,13 @@ class BlankNode extends Node {
console.log('Bad blank id:', id)
throw new Error('Bad id argument to new blank node: ' + id)
}
if (id.includes('#')) {
// Is a URI with hash fragment
let fragments = id.split('#')
id = fragments[fragments.length - 1]

this.id = BlankNode.normalizeID(id)

const existing = Term.bnMap[this.id]
if (existing) {
return existing
}
this.id = id
// this.id = '' + BlankNode.nextId++
} else {
this.id = 'n' + BlankNode.nextId++
}
Expand Down Expand Up @@ -52,7 +63,7 @@ class BlankNode extends Node {
return '_:' + this.value
}

toString () {
generateString () {
return BlankNode.NTAnonymousNodePrefix + this.id
}
}
Expand Down
16 changes: 7 additions & 9 deletions src/data-factory.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
'use strict'
const BlankNode = require('./blank-node')
const Collection = require('./collection')
const DefaultGraph = require('./default-graph')
const Fetcher = require('./fetcher')
const IndexedFormula = require('./store')
const Literal = require('./literal')
const NamedNode = require('./named-node')
const Statement = require('./statement')
const Term = require('./term')
const Variable = require('./variable')

function blankNode (value) {
return new BlankNode(value)
return Term.blankNodeByID(value)
}
function collection (elements) {
return new Collection(elements)
Expand All @@ -25,21 +23,21 @@ function graph () {
return new IndexedFormula()
}
function lit (val, lang, dt) {
return new Literal('' + val, lang, dt)
return Term.literalByValue('' + val, lang, dt)
}
function literal (value, languageOrDatatype) {
if (typeof languageOrDatatype === 'string') {
if (languageOrDatatype.indexOf(':') === -1) {
return new Literal(value, languageOrDatatype)
return Term.literalByValue(value, languageOrDatatype)
} else {
return new Literal(value, null, namedNode(languageOrDatatype))
return Term.literalByValue(value, null, namedNode(languageOrDatatype))
}
} else {
return new Literal(value, null, languageOrDatatype)
return Term.literalByValue(value, null, languageOrDatatype)
}
}
function namedNode (value) {
return new NamedNode(value)
return Term.namedNodeByIRI(value)
}
function quad (subject, predicate, object, graph) {
graph = graph || new DefaultGraph()
Expand Down
3 changes: 2 additions & 1 deletion src/fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const Namespace = require('./namespace')
const rdfParse = require('./parse')
const parseRDFaDOM = require('./rdfaparser').parseRDFaDOM
const RDFParser = require('./rdfxmlparser')
const Term = require('./term')
const Uri = require('./uri')
const Util = require('./util')
const serialize = require('./serialize')
Expand Down Expand Up @@ -1066,7 +1067,7 @@ class Fetcher {
*/
putBack (uri, options = {}) {
uri = uri.uri || uri // Accept object or string
let doc = new NamedNode(uri).doc() // strip off #
let doc = Term.namedNodeByIRI(uri).doc() // strip off #
options.contentType = options.contentType || 'text/turtle'
options.data = serialize(doc, this.store, doc.uri, options.contentType)
return this.webOperation('PUT', uri, options)
Expand Down
18 changes: 9 additions & 9 deletions src/formula.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
'use strict'
const BlankNode = require('./blank-node')

const ClassOrder = require('./class-order')
const Collection = require('./collection')
const Literal = require('./literal')
const log = require('./log')
const NamedNode = require('./named-node')
const Node = require('./node')
const Serializer = require('./serialize')
const Statement = require('./statement')
const Term = require('./term')
const Variable = require('./variable')

/** @module formula */
Expand Down Expand Up @@ -44,7 +44,7 @@ class Formula extends Node {
return this.statements.push(st)
}
bnode (id) {
return new BlankNode(id)
return Term.blankNodeByID(id)
}

addAll (statements) {
Expand Down Expand Up @@ -365,9 +365,9 @@ class Formula extends Node {
}
/** Trace statements which connect directly, or through bnodes
*
* @param {NamedNode} subject - The node to start looking for statments
* @param {NamedNode} subject - The node to start looking for statements
* @param {NamedNode} doc - The document to be searched, or null to search all documents
* @returns an array of statements, duplicate statements are suppresssed.
* @returns an array of statements, duplicate statements are suppressed.
*/
connectedStatements (subject, doc, excludePredicateURIs) {
excludePredicateURIs = excludePredicateURIs || []
Expand Down Expand Up @@ -413,7 +413,7 @@ class Formula extends Node {
* Transforms an NTriples string format into a Node.
* The bnode bit should not be used on program-external values; designed
* for internal work such as storing a bnode id in an HTML attribute.
* This will only parse the strings generated by the vaious toNT() methods.
* This will only parse the strings generated by the various toNT() methods.
*/
fromNT (str) {
var dt, k, lang
Expand All @@ -439,7 +439,7 @@ class Formula extends Node {
str = str.replace(/\\\\/g, '\\')
return this.literal(str, lang, dt)
case '_':
return new BlankNode(str.slice(2))
return Term.blankNodeByID(str.slice(2))
case '?':
return new Variable(str.slice(1))
}
Expand Down Expand Up @@ -480,7 +480,7 @@ class Formula extends Node {
return collection
}
literal (val, lang, dt) {
return new Literal('' + val, lang, dt)
return Term.literalByValue('' + val, lang, dt)
}
/**
* transform a collection of NTriple URIs into their URI strings
Expand Down Expand Up @@ -541,7 +541,7 @@ class Formula extends Node {
if (name) {
throw new Error('This feature (kb.sym with 2 args) is removed. Do not assume prefix mappings.')
}
return new NamedNode(uri)
return Term.namedNodeByIRI(uri)
}
the (s, p, o, g) {
var x = this.any(s, p, o, g)
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var $rdf = {
N3Parser: require('./n3parser'),
NamedNode: require('./named-node'),
Namespace: require('./namespace'),
ns: require('./ns'),
Node: require('./node'),
parse: require('./parse'),
Query: require('./query').Query,
Expand All @@ -25,6 +26,7 @@ var $rdf = {
sparqlUpdateParser: require('./patch-parser'),
Statement: require('./statement'),
term: require('./node').fromValue,
Term: require('./term'),
UpdateManager: require('./update-manager'),
UpdatesSocket: require('./updates-via').UpdatesSocket,
UpdatesVia: require('./updates-via').UpdatesVia,
Expand Down
36 changes: 13 additions & 23 deletions src/literal.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict'
const ClassOrder = require('./class-order')
const NamedNode = require('./named-node')
const Node = require('./node')
const Term = require('./term')
const XSD = require('./xsd')

class Literal extends Node {
class Literal extends Term {
constructor (value, language, datatype) {
super()
this.termType = Literal.termType
Expand All @@ -14,22 +14,15 @@ class Literal extends Node {
datatype = XSD.langString
}
// If not specified, a literal has the implied XSD.string default datatype
if (datatype) {
this.datatype = NamedNode.fromValue(datatype)
this.datatype = datatype ? NamedNode.fromValue(datatype) : XSD.string

const existing = Term.findLiteralByValue(value, language, datatype)
if (existing) {
return existing
}
}
copy () {
Copy link
Collaborator Author

@rescribet rescribet Nov 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method (copy) seems rather nonsensical now, since indexing is done with the value, so either reindexing logic has to be created, or the user should pass intended modifications to this function.

return new Literal(this.value, this.lang, this.datatype)
}
equals (other) {
if (!other) {
return false
}
return (this.termType === other.termType) &&
(this.value === other.value) &&
(this.language === other.language) &&
((!this.datatype && !other.datatype) ||
(this.datatype && this.datatype.equals(other.datatype)))
return Term.literalByValue(this.value, this.lang, this.datatype)
}
get language () {
return this.lang
Expand All @@ -52,15 +45,12 @@ class Literal extends Node {

if (this.language) {
str += '@' + this.language
} else if (!this.datatype.equals(XSD.string)) {
} else if (this.datatype !== XSD.string) {
// Only add datatype if it's not a string
str += '^^' + this.datatype.toCanonical()
}
return str
}
toString () {
return '' + this.value
}
/**
* @method fromBoolean
* @static
Expand All @@ -69,7 +59,7 @@ class Literal extends Node {
*/
static fromBoolean (value) {
let strValue = value ? '1' : '0'
return new Literal(strValue, null, XSD.boolean)
return Term.literalByValue(strValue, null, XSD.boolean)
}
/**
* @method fromDate
Expand All @@ -87,7 +77,7 @@ class Literal extends Node {
let date = '' + value.getUTCFullYear() + '-' + d2(value.getUTCMonth() + 1) +
'-' + d2(value.getUTCDate()) + 'T' + d2(value.getUTCHours()) + ':' +
d2(value.getUTCMinutes()) + ':' + d2(value.getUTCSeconds()) + 'Z'
return new Literal(date, null, XSD.dateTime)
return Term.literalByValue(date, null, XSD.dateTime)
}
/**
* @method fromNumber
Expand All @@ -106,7 +96,7 @@ class Literal extends Node {
} else {
datatype = XSD.double
}
return new Literal(strValue, null, datatype)
return Term.literalByValue(strValue, null, datatype)
}
/**
* @method fromValue
Expand All @@ -130,7 +120,7 @@ class Literal extends Node {
case 'number':
return Literal.fromNumber(value)
case 'string':
return new Literal(value)
return Term.literalByValue(value, null, XSD.string)
}
throw new Error("Can't make literal from " + value + ' of type ' +
typeof value)
Expand Down
31 changes: 15 additions & 16 deletions src/named-node.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict'
const ClassOrder = require('./class-order')
const Node = require('./node')
const Term = require('./term')

/**
* @class NamedNode
* @extends Node
* @extends Term
*/
class NamedNode extends Node {
class NamedNode extends Term {
/**
* @constructor
* @param iri {String}
Expand All @@ -32,6 +32,11 @@ class NamedNode extends Node {
throw new Error(message)
}

const existing = Term.nsMap[iri]
if (existing) {
return existing
}

this.value = iri
}
/**
Expand All @@ -42,7 +47,7 @@ class NamedNode extends Node {
var p = str.slice(0, -1).lastIndexOf('/')
var q = str.indexOf('//')
if ((q >= 0 && p < q + 2) || p < 0) return null
return new NamedNode(str.slice(0, p + 1))
return Term.namedNodeByIRI(str.slice(0, p + 1))
}
/**
* Returns an NN for the whole web site, ending in slash.
Expand All @@ -54,17 +59,18 @@ class NamedNode extends Node {
if (p < 0) throw new Error('This URI does not have a web site part (origin)')
var q = str.indexOf('/', p+2)
if (q < 0) throw new Error('This URI does not have a web site part. (origin)')
return new NamedNode(str.slice(0, q + 1))
return Term.namedNodeByIRI(str.slice(0, q + 1))
}
doc () {
if (this.uri.indexOf('#') < 0) {
return this
} else {
return new NamedNode(this.uri.split('#')[0])
return Term.namedNodeByIRI(this.uri.split('#')[0])
}
}
toString () {
return '<' + this.uri + '>'

generateString () {
return '<' + this.uri + '>'
}

/**
Expand All @@ -77,14 +83,7 @@ class NamedNode extends Node {
this.value = uri
}
static fromValue (value) {
if (typeof value === 'undefined' || value === null) {
return value
}
const isNode = value && value.termType
if (isNode) {
return value
}
return new NamedNode(value)
return Term.namedNodeByIRI(value)
}
}
NamedNode.termType = 'NamedNode'
Expand Down
8 changes: 6 additions & 2 deletions src/namespace.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
const NamedNode = require('./named-node')
const Term = require('./term');

function Namespace (nsuri) {
Term.namedNodeByIRI(nsuri)

return function (ln) {
return new NamedNode(nsuri + (ln || ''))
const fullIRI = nsuri + (ln || '')

return Term.namedNodeByIRI(fullIRI, ln)
}
}

Expand Down
Loading