Skip to content

Commit

Permalink
Finish 3.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
gkellogg committed Sep 1, 2023
2 parents 8dbd85e + 260fd6f commit e139083
Show file tree
Hide file tree
Showing 22 changed files with 239 additions and 257 deletions.
14 changes: 4 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,10 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby:
- 2.6
- 2.7
- "3.0"
- 3.1
- ruby-head
- jruby
ruby: ['3.0', 3.1, 3.2, ruby-head, jruby]
steps:
- name: Clone repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
Expand All @@ -38,7 +32,7 @@ jobs:
- name: Run tests
run: ruby --version; bundle exec rspec spec || $ALLOW_FAILURES
- name: Coveralls GitHub Action
uses: coverallsapp/github-action@v1.1.2
if: "matrix.ruby == '3.0'"
uses: coverallsapp/github-action@v2
if: "matrix.ruby == '3.2'"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/generate-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
name: Update gh-pages with docs
steps:
- name: Clone repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ group :development, :test do
gem 'rdf-xsd', git: "https://github.com/ruby-rdf/rdf-xsd", branch: "develop"
gem 'sxp', git: "https://github.com/dryruby/sxp.rb", branch: "develop"
gem "redcarpet", platforms: :ruby
gem 'simplecov', '~> 0.21', platforms: :mri
gem 'simplecov', '~> 0.22', platforms: :mri
gem 'simplecov-lcov', '~> 0.8', platforms: :mri
end
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[Turtle][] reader/writer for [RDF.rb][RDF.rb] .

[![Gem Version](https://badge.fury.io/rb/rdf-turtle.png)](https://badge.fury.io/rb/rdf-turtle)
[![Gem Version](https://badge.fury.io/rb/rdf-turtle.svg)](https://badge.fury.io/rb/rdf-turtle)
[![Build Status](https://github.com/ruby-rdf/rdf-turtle/workflows/CI/badge.svg?branch=develop)](https://github.com/ruby-rdf/rdf-turtle/actions?query=workflow%3ACI)
[![Coverage Status](https://coveralls.io/repos/ruby-rdf/rdf-turtle/badge.svg?branch=develop)](https://coveralls.io/github/ruby-rdf/rdf-turtle?branch=develop)
[![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf)
Expand All @@ -17,9 +17,9 @@ Install with `gem install rdf-turtle`

* 100% free and unencumbered [public domain](https://unlicense.org/) software.
* Implements a complete parser for [Turtle][].
* Compatible with Ruby >= 2.6.
* Compatible with Ruby >= 3.0.
* Optional streaming writer, to serialize large graphs
* Provisional support for [Turtle-star][RDF-star].
* Provisional support for [RDF 1.2][].

## Usage
Instantiate a reader from a local file:
Expand All @@ -36,23 +36,25 @@ Write a graph to a file:
writer << graph
end

## Turtle-star (RDF-star)
## RDF 1.2

Both reader and writer include provisional support for [Turtle-star][RDF-star].
Both reader and writer include provisional support for [RDF 1.2][] quoted triples.

Both reader and writer include provisional support for [RDF 1.2][] directional language-tagged strings, which are literals of type `rdf:dirLangString` having both a `language` and `direction`.

Internally, an `RDF::Statement` is treated as another resource, along with `RDF::URI` and `RDF::Node`, which allows an `RDF::Statement` to have a `#subject` or `#object` which is also an `RDF::Statement`.

**Note: This feature is subject to change or elimination as the standards process progresses.**

### Serializing a Graph containing embedded statements
### Serializing a Graph containing quoted triples

require 'rdf/turtle'
statement = RDF::Statement(RDF::URI('bob'), RDF::Vocab::FOAF.age, RDF::Literal(23))
graph = RDF::Graph.new << [statement, RDF::URI("ex:certainty"), RDF::Literal(0.9)]
graph.dump(:ttl, validate: false, standard_prefixes: true)
# => '<<<bob> foaf:age 23>> <ex:certainty> 9.0e-1 .'

### Reading a Graph containing embedded statements
### Reading a Graph containing quoted triples

By default, the Turtle reader will reject a document containing a subject resource.

Expand Down Expand Up @@ -140,9 +142,9 @@ This version uses a hand-written parser using the Lexer from the [EBNF][] gem in

## Dependencies

* [Ruby](https://ruby-lang.org/) (>= 2.6)
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.2)
* [EBNF][] (~> 1.2)
* [Ruby](https://ruby-lang.org/) (>= 3.0)
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.3)
* [EBNF][] (~> 2.4)

## Installation

Expand Down Expand Up @@ -187,10 +189,10 @@ A copy of the [Turtle EBNF][] and derived parser files are included in the repos
[PDD]: https://unlicense.org/#unlicensing-contributions
[RDF.rb]: https://ruby-rdf.github.io/rdf
[EBNF]: https://rubygems.org/gems/ebnf
[Backports]: https://rubygems.org/gems/backports
[N-Triples]: https://www.w3.org/TR/rdf-testcases/#ntriples
[Turtle]: https://www.w3.org/TR/2012/WD-turtle-20120710/
[RDF-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html
[N-Triples]: https://www.w3.org/TR/rdf12-n-triples
[Turtle]: https://www.w3.org/TR/rdf12-turtle
[RDF 1.1]: https://www.w3.org/TR/rdf11-concepts/
[RDF 1.2]: https://www.w3.org/TR/rdf12-concepts/
[Turtle doc]: https://ruby-rdf.github.io/rdf-turtle/master/file/README.md
[Turtle EBNF]: https://dvcs.w3.org/hg/rdf/file/default/rdf-turtle/turtle.bnf
[Freebase Dumps]: https://developers.google.com/freebase/data
[Turtle EBNF]: https://w3c.github.io/rdf-turtle/spec/turtle-bnf.html
[Freebase Dumps]: https://developers.google.com/freebase/data
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.1
3.3.0
142 changes: 71 additions & 71 deletions etc/turtle.bnf
Original file line number Diff line number Diff line change
@@ -1,74 +1,74 @@
[1] turtleDoc ::= statement*
[2] statement ::= directive | triples "."
[3] directive ::= prefixID | base | sparqlPrefix | sparqlBase
[4] prefixID ::= PREFIX PNAME_NS IRIREF "."?
[5] base ::= BASE IRIREF "."?
[5s] sparqlPrefix ::= "PREFIX" PNAME_NS IRIREF
[6s] parqlBase ::= "BASE" IRIREF
[6] triples ::= subject predicateObjectList | blankNodePropertyList predicateObjectList?
[7] predicateObjectList ::= verb objectList ( ";" ( verb objectList)? )*
[8] objectList ::= object annotation? ( "," object annotation? )*
[9] verb ::= predicate | "a"
[10] subject ::= iri | BlankNode | collection | quotedTriple
[11] predicate ::= iri
[12] object ::= iri | BlankNode | collection | blankNodePropertyList | literal | quotedTriple
[13] literal ::= RDFLiteral | NumericLiteral | BooleanLiteral
[14] blankNodePropertyList ::= "[" predicateObjectList "]"
[15] collection ::= "(" object* ")"
[16] NumericLiteral ::= INTEGER | DECIMAL | DOUBLE
[128s] RDFLiteral ::= String ( LANGTAG | ( "^^" iri ) )?
[133s] BooleanLiteral ::= "true" | "false"
[17] String ::= STRING_LITERAL_QUOTE | STRING_LITERAL_SINGLE_QUOTE | STRING_LITERAL_LONG_SINGLE_QUOTE |
STRING_LITERAL_LONG_QUOTE
[135s] iri ::= IRIREF | PrefixedName
[136s] PrefixedName ::= PNAME_LN | PNAME_NS
[137s] BlankNode ::= BLANK_NODE_LABEL | ANON
[27] quotedTriple ::= "<<" qtSubject predicate qtObject ">>"
[28] qtSubject ::= iri | BlankNode | quotedTriple
[29] qtObject ::= iri | BlankNode | literal | quotedTriple
[30] annotation ::= '{|' predicateObjectList '|}'
turtleDoc ::= statement*
statement ::= directive | triples "."
directive ::= prefixID | base | sparqlPrefix | sparqlBase
prefixID ::= PREFIX PNAME_NS IRIREF "."?
base ::= BASE IRIREF "."?
sparqlPrefix ::= "PREFIX" PNAME_NS IRIREF
parqlBase ::= "BASE" IRIREF
triples ::= subject predicateObjectList | blankNodePropertyList predicateObjectList?
predicateObjectList ::= verb objectList ( ";" ( verb objectList)? )*
objectList ::= object annotation? ( "," object annotation? )*
verb ::= predicate | "a"
subject ::= iri | BlankNode | collection | quotedTriple
predicate ::= iri
object ::= iri | BlankNode | collection | blankNodePropertyList | literal | quotedTriple
literal ::= RDFLiteral | NumericLiteral | BooleanLiteral
blankNodePropertyList ::= "[" predicateObjectList "]"
collection ::= "(" object* ")"
NumericLiteral ::= INTEGER | DECIMAL | DOUBLE
RDFLiteral ::= String ( LANG_DIR | ( "^^" iri ) )?
BooleanLiteral ::= "true" | "false"
String ::= STRING_LITERAL_QUOTE | STRING_LITERAL_SINGLE_QUOTE | STRING_LITERAL_LONG_SINGLE_QUOTE |
STRING_LITERAL_LONG_QUOTE
iri ::= IRIREF | PrefixedName
PrefixedName ::= PNAME_LN | PNAME_NS
BlankNode ::= BLANK_NODE_LABEL | ANON
quotedTriple ::= "<<" qtSubject predicate qtObject ">>"
qtSubject ::= iri | BlankNode | quotedTriple
qtObject ::= iri | BlankNode | literal | quotedTriple
annotation ::= '{|' predicateObjectList '|}'

@terminals

[18] IRIREF ::= '<' ([^#x00-#x20<>"{}|^`\] | UCHAR)* '>'
[139s] PNAME_NS ::= PN_PREFIX? ":"
[140s] PNAME_LN ::= PNAME_NS PN_LOCAL
[141s] BLANK_NODE_LABEL ::= '_:' ( PN_CHARS_U | [0-9] ) ((PN_CHARS|'.')* PN_CHARS)?
[144s] LANGTAG ::= "@" [a-zA-Z]+ ( "-" [a-zA-Z0-9]+ )*
[19] INTEGER ::= [+-]? [0-9]+
[20] DECIMAL ::= [+-]? ( ([0-9])* '.' ([0-9])+ )
[21] DOUBLE ::= [+-]? ( [0-9]+ '.' [0-9]* EXPONENT | '.' ([0-9])+ EXPONENT | ([0-9])+ EXPONENT )
[154s] EXPONENT ::= [eE] [+-]? [0-9]+
[22] STRING_LITERAL_QUOTE ::= '"' ( [^#x22#x5C#xA#xD] | ECHAR | UCHAR )* '"'
[23] STRING_LITERAL_SINGLE_QUOTE ::= "'" ( [^#x27#x5C#xA#xD] | ECHAR | UCHAR )* "'"
[24] STRING_LITERAL_LONG_SINGLE_QUOTE ::= "'''" ( ( "'" | "''" )? ( [^'\] | ECHAR | UCHAR ) )* "'''"
[25] STRING_LITERAL_LONG_QUOTE ::= '"""' ( ( '"' | '""' )? ( [^"\] | ECHAR | UCHAR ) )* '"""'
[26] UCHAR ::= ( "\u" HEX HEX HEX HEX ) | ( "\U" HEX HEX HEX HEX HEX HEX HEX HEX )
[159s] ECHAR ::= "\" [tbnrf\"']
[28t] PREFIX ::= "@"?[Pp][Rr][Ee][Ff][Ii][Xx]
[29t] BASE ::= "@"?[Bb][Aa][Ss][Ee]
[161s] WS ::= #x20 | #x9 | #xD | #xA
[162s] ANON ::= "[" WS* "]"
[163s] PN_CHARS_BASE ::= [A-Z]
| [a-z]
| [#x00C0-#x00D6]
| [#x00D8-#x00F6]
| [#x00F8-#x02FF]
| [#x0370-#x037D]
| [#x037F-#x1FFF]
| [#x200C-#x200D]
| [#x2070-#x218F]
| [#x2C00-#x2FEF]
| [#x3001-#xD7FF]
| [#xF900-#xFDCF]
| [#xFDF0-#xFFFD]
| [#x10000-#xEFFFF]
[164s] PN_CHARS_U ::= PN_CHARS_BASE | '_'
[166s] PN_CHARS ::= PN_CHARS_U | "-" | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]
[167s] PN_PREFIX ::= PN_CHARS_BASE ( ( PN_CHARS | "." )* PN_CHARS )?
[168s] PN_LOCAL ::= ( PN_CHARS_U | ':' | [0-9] | PLX ) ( ( PN_CHARS | '.' | ':' | PLX )* ( PN_CHARS | ':' | PLX ) ) ?
[169s] PLX ::= PERCENT | PN_LOCAL_ESC
[170s] PERCENT ::= '%' HEX HEX
[171s] HEX ::= [0-9] | [A-F] | [a-f]
[172s] PN_LOCAL_ESC ::= '\' ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '='
| '/' | '?' | '#' | '@' | '%' )
IRIREF ::= '<' ([^#x00-#x20<>"{}|^`\] | UCHAR)* '>'
PNAME_NS ::= PN_PREFIX? ":"
PNAME_LN ::= PNAME_NS PN_LOCAL
BLANK_NODE_LABEL ::= '_:' ( PN_CHARS_U | [0-9] ) ((PN_CHARS|'.')* PN_CHARS)?
LANG_DIR ::= "@" [a-zA-Z]+ ( "-" [a-zA-Z0-9]+ )* ('--' [a-zA-Z]+)?`
INTEGER ::= [+-]? [0-9]+
DECIMAL ::= [+-]? ( ([0-9])* '.' ([0-9])+ )
DOUBLE ::= [+-]? ( [0-9]+ '.' [0-9]* EXPONENT | '.' ([0-9])+ EXPONENT | ([0-9])+ EXPONENT )
EXPONENT ::= [eE] [+-]? [0-9]+
STRING_LITERAL_QUOTE ::= '"' ( [^#x22#x5C#xA#xD] | ECHAR | UCHAR )* '"'
STRING_LITERAL_SINGLE_QUOTE ::= "'" ( [^#x27#x5C#xA#xD] | ECHAR | UCHAR )* "'"
STRING_LITERAL_LONG_SINGLE_QUOTE ::= "'''" ( ( "'" | "''" )? ( [^'\] | ECHAR | UCHAR ) )* "'''"
STRING_LITERAL_LONG_QUOTE ::= '"""' ( ( '"' | '""' )? ( [^"\] | ECHAR | UCHAR ) )* '"""'
UCHAR ::= ( "\u" HEX HEX HEX HEX ) | ( "\U" HEX HEX HEX HEX HEX HEX HEX HEX )
ECHAR ::= "\" [tbnrf\"']
PREFIX ::= "@"?[Pp][Rr][Ee][Ff][Ii][Xx]
BASE ::= "@"?[Bb][Aa][Ss][Ee]
WS ::= #x20 | #x9 | #xD | #xA
ANON ::= "[" WS* "]"
PN_CHARS_BASE ::= [A-Z]
| [a-z]
| [#x00C0-#x00D6]
| [#x00D8-#x00F6]
| [#x00F8-#x02FF]
| [#x0370-#x037D]
| [#x037F-#x1FFF]
| [#x200C-#x200D]
| [#x2070-#x218F]
| [#x2C00-#x2FEF]
| [#x3001-#xD7FF]
| [#xF900-#xFDCF]
| [#xFDF0-#xFFFD]
| [#x10000-#xEFFFF]
PN_CHARS_U ::= PN_CHARS_BASE | '_'
PN_CHARS ::= PN_CHARS_U | "-" | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]
PN_PREFIX ::= PN_CHARS_BASE ( ( PN_CHARS | "." )* PN_CHARS )?
PN_LOCAL ::= ( PN_CHARS_U | ':' | [0-9] | PLX ) ( ( PN_CHARS | '.' | ':' | PLX )* ( PN_CHARS | ':' | PLX ) ) ?
PLX ::= PERCENT | PN_LOCAL_ESC
PERCENT ::= '%' HEX HEX
HEX ::= [0-9] | [A-F] | [a-f]
PN_LOCAL_ESC ::= '\' ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '='
| '/' | '?' | '#' | '@' | '%' )
5 changes: 3 additions & 2 deletions lib/rdf/turtle/freebase_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ def read_literal
if literal_str = match(LITERAL_PLAIN)
literal_str = self.class.unescape(literal_str)
literal = case
when language = match(RDF::NTriples::Reader::LANGTAG)
RDF::Literal.new(literal_str, language: language)
when lang_dir = match(RDF::Turtle::Reader::LANG_DIR)
language, direction = lang_dir.split('--')
RDF::Literal.new(literal_str, language: language, direction: direction)
when datatype = match(/^(\^\^)/)
RDF::Literal.new(literal_str, datatype: read_pname(intern: true) || read_uriref || fail_object)
else
Expand Down
16 changes: 10 additions & 6 deletions lib/rdf/turtle/reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Reader < RDF::Reader

terminal(:PREFIX, PREFIX)
terminal(:BASE, BASE)
terminal(:LANGTAG, LANGTAG)
terminal(:LANG_DIR, LANG_DIR)

##
# Reader options
Expand Down Expand Up @@ -394,7 +394,7 @@ def read_object(subject = nil, predicate = nil)
end
end

# Read an RDF-star reified statement
# Read a quoted triple
# @return [RDF::Statement]
def read_quotedTriple
return unless @options[:rdfstar]
Expand Down Expand Up @@ -470,8 +470,10 @@ def read_literal
value = @lexer.shift.value[1..-2]
error("read_literal", "Unexpected end of file") unless token = @lexer.first
case token.type || token.value
when :LANGTAG
literal(value, language: @lexer.shift.value[1..-1].to_sym)
when :LANG_DIR
lang_dir = @lexer.shift.value[1..-1]
language, direction = lang_dir.split('--')
literal(value, language: language, direction: direction)
when '^^'
@lexer.shift
literal(value, datatype: read_iri)
Expand All @@ -484,8 +486,10 @@ def read_literal
value = @lexer.shift.value[3..-4]
error("read_literal", "Unexpected end of file") unless token = @lexer.first
case token.type || token.value
when :LANGTAG
literal(value, language: @lexer.shift.value[1..-1].to_sym)
when :LANG_DIR
lang_dir = @lexer.shift.value[1..-1]
language, direction = lang_dir.split('--')
literal(value, language: language, direction: direction)
when '^^'
@lexer.shift
literal(value, datatype: read_iri)
Expand Down
2 changes: 1 addition & 1 deletion lib/rdf/turtle/terminals.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ module Terminals
# 141s
BLANK_NODE_LABEL = /_:(?:[0-9]|#{PN_CHARS_U})(?:(?:#{PN_CHARS}|\.)*#{PN_CHARS})?/u.freeze
# 144s
LANGTAG = /@[a-zA-Z]+(?:-[a-zA-Z0-9]+)*/u.freeze
LANG_DIR = /@([a-zA-Z]+(?:-[a-zA-Z0-9]+)*(?:--[a-zA-Z]+)?)/u.freeze
# 19
INTEGER = /[+-]?[0-9]+/u.freeze
# 20
Expand Down
9 changes: 5 additions & 4 deletions lib/rdf/turtle/writer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def self.options
symbol: :literal_shorthand,
datatype: FalseClass,
on: ["--no-literal-shorthand"],
description: "Do not ttempt to use Literal shorthands fo numbers and boolean values.") {false},
description: "Do not attempt to use Literal shorthands fo numbers and boolean values.") {false},
]
end

Expand Down Expand Up @@ -215,7 +215,7 @@ def get_pname(resource)
end

pname = case
when @uri_to_pname.has_key?(uri)
when @uri_to_pname.key?(uri)
return @uri_to_pname[uri]
when u = @uri_to_prefix.keys.sort_by {|uu| uu.length}.reverse.detect {|uu| uri.index(uu.to_s) == 0}
# Use a defined prefix
Expand Down Expand Up @@ -296,8 +296,9 @@ def format_literal(literal, **options)
in_form ? literal.value : literal.canonicalize.to_s.sub('E', 'e').to_s
else
text = quoted(literal.value)
text << "@#{literal.language}" if literal.has_language?
text << "^^#{format_uri(literal.datatype)}" if literal.has_datatype?
text << "@#{literal.language}" if literal.language?
text << "--#{literal.direction}" if literal.direction?
text << "^^#{format_uri(literal.datatype)}" if literal.datatype?
text
end
else
Expand Down
Loading

0 comments on commit e139083

Please sign in to comment.