1
2 /**
3 Name: RDEL ("Ardele") - Resource Decription Extraction/Execution Language
4 Description: A mainly functional RDF programming language
5 .. Inspired by python, n3 and haskell, with a little js sprinkled in.
6 .. All statically typed with inference.
7
8 - Curie (and uri) names are public and shared.
9 The rest are local (to scope), instrumental names (though somewhat akin to bnodes..).
10
11 - Top-level functions (def) represent "subject predicate", use like some:expr() ?object
12 - Class-level functions represent "predicate", use like subject some:expr() ?object
13
14 - Use "name = <expr>" as shorthand+side-effect(!) for "one s p ?name else unknown '...' "
15 - side-effect: scopes name at same level as <expr> (as opposed to match expressions)
16 - for now, these assignments *are the only way* to set "same scope level" values..
17 - NOTE: "=" *is not* shorthand for owl:sameAs (as in n3); it has side-effect as explained
18
19 - If some:expr() is used without name for object, expression represents obj,
20 - only useful when assigning (see = above) and to send as parameters to functions
21
22 - Generally, expressions with two atoms are of the form
23 "subject predicate" and represents the object,
24 to get subject, you need "is predicate of object". Nest with parens..
25
26 - Datatypes. First, the ones from rdf:
27 - resources with properties. kind of a no-brainer eh? ;)
28 - literals (xsd:*-things are built-in)
29 - collection lists
30 Beyond these are the instrumental:
31 - functions (see above) - these are the core of RDEL!
32 - sets are collections rdf-wise, but keeps unique values (in a no-uniqueness world.. ;D)
33 - @TODO: no traditional dictionaries - use real rdf.. but perhaps.. named tuples?
34 Or "unpack" - see below?
35
36 - Declaring new values will (just as assignments) be local to the runtime
37 (@TODO:context? see below) including desc constructs (proper sub-graphs?).
38
39 - Builtin prefixes are: rdf:, rdfs:, owl:, xsd:, std:
40 .. can of course be redefined, as here (std: to :)
41
42 - std: is the core of the stdlib and contains a bunch of things, like:
43 def std:print() ...
44 def std:<time/now>() ...
45 def std:<posix/stat>() ...
46 class xsd:string
47 def std:expand() ...
48 */
49
50
51 prefix : <http://oort.to/ts/2008/08/rdel/stdlib/>
52 prefix l: <http://neverspace.net/ts/2008/08/rdel/testlib#>
53 prefix foaf: <http://xmlns.com/foaf/0.1/>
54
55
56 def l:sum_ages()
57 var sum = 0 # use var for mutable values for names (only for instrumental names)
58 now = :<time/now>() # all other names are final (though their refs belong to the open world..)
59 each ?person a foaf:Person
60 # could also use (but may fail with unknown):
61 #sum += age(now, person foaf:birthday)
62 one person foaf:birthday ?bday
63 sum += age(now, bday)
64 return sum
65
66 # Sketch for opt. delimiters (non-indented stuff):
67 def l:sum_ages() ->
68 var sum = 0; now = :<time/now>()
69 each ?person a foaf:Person ->
70 one person foaf:birthday ?bday -> sum += age(now, bday) .
71 .
72 return sum
73 .
74 # On one line:
75 def l:sum_ages() -> var sum = 0; now = :<time/now>(); each ?person a foaf:Person -> one person foaf:birthday ?bday -> sum += age(now, bday) .. return sum .
76 # use functions as values:
77 var increased = :apply( [] , def (it)-> return it + 1 . )
78
79
80 # private name
81 def age(from as xsd:dateTime, to as xsd:dateTime)
82 return to - from # @TODO: as shorthand for: to std:subtract(from)?
83 # @TODO: if bad type/datatype, complement to "unknown" called "mismatch"?
84 # .. but that should be dealt with by caller!
85
86 def l:allDescriptions()
87 each ?thing a owl:Thing
88 yield thing l:info() or continue # @TODO: odd? "except continue"?
89
90
91 class foaf:Person
92 def l:info()
93 one self foaf:homepage ?homepage
94 return homepage rdfs:label + homepage rdfs:comment
95 # @TODO: shorthand for (hp label) std:add (hp comment)?
96 except
97 unknown "No described homepage for {self}"@en :expand()
98
99 # @TODO: classes as "statemachines" too? with e.g. var someState (note: *no* class-level statics)
100 # .. for what should the state be kept? The call chain? The context?
101
102
103 # exported "constant"
104 l:message = "An english literal."@en
105
106
107 # Add data in current context/scope (also see "load" etc.):
108 # This looks crude (but should work?):
109 item rdfs:label "some item"; item rdfs:comment "this isn't turtle!"
110 # This may be better..:
111 item desc
112 rdfs:label "some item"@en
113 rdfs:comment "..."
114 rdfs:seeAlso <./> desc
115 rdfs:label "this module dir"@en
116 rdfs:seeAlso desc # bnode
117 rdfs:label "related"@en
118 rdfs:seeAlso desc -> rdfs:label "more"@en .
119 # @rejected: Alt. is to have e.g. a "turtle" block, for a given scope. But that
120 # will make syntax inconsistent - i.e. hard to spot the suble difference
121 # between turtle/n3 and RDEL..
122 # @TODO: Perhaps allow: load """ .. n3 in opaque string literal .. """, 'text/n3'
123
124 # @TODO: what about "call with context"; to process some things in a local
125 # context (as opposed to currently, only "global graph")
126
127 # TODO: "class instantiation" use case:
128 prefix html :<html/>
129 parser desc
130 a html:HtmlParser
131 html:parseMode "tagsoup"
132 parser html:parse(<file:test/data/1.html>)
133 # one-liner:
134 parser desc -> a html:HtmlParser; html:parseMode "tagsoup" .
135
136 # TODO: or via factory (requires a new instance equiv. to class.):
137 html:HtmlParser desc # TODO: can we do this!?
138 def :new(parseMode="strict")
139 return desc
140 a self
141 html:parseMode parseMode
142 # usage:
143 parser = html:HtmlParser :new("tagsoup")
144 # .. sugar?:
145 parser = html:HtmlParser("tagsoup")
146
147 # this may also be a powerful data notation in general..
148
149 # as "del" in python:
150 forget parser
151
152
153 main
154
155 :print("Testing stuff, e.g. printing info about {foaf:Person}s." :expand())
156
157 load <file:rdel_test_data.n3>
158
159 me = <http://purl.org/NET/dust/me#self>
160 :print( me l:info() )
161 # @TODO: what about: label = me rdfs:label for @{lang} ?
162
163 # Verbose:
164 one l:sum_ages() ?sum
165 :print(sum)
166 except
167 unknown "..."
168 # More compact (*and* declares sum in scope (if successful)):
169 sum = l:sum_ages()
170 :print( sum )
171 # Most compact:
172 :print( l:sum_ages() )
173
174 list = [:a, :b] # @TODO: cannot use n3 syntax (:a :b) since space is expression segment separator!
175 list :append(me)
176 # TODO: how to loop over lists? Overload each (it's a patter matcher!)? Rather "for... in":
177 for item in list
178 :print(item)
179
180 var counter = 0
181 for descr in l:allDescriptions()
182 counter += 1
183 :print("{counter}: {descr}" :expand())
184
185
186 # List comprehension (long version omitted):
187 hasSites = [?person each ?person a foaf:Person if ?person foaf:homepage]
188 # Generator comprehension:
189 hasSites = ?person each ?person a foaf:Person if ?person foaf:homepage
190 # which is a shorthand for:
191 hasSites = (def () -> each ?person a foaf:Person -> if ?person foaf:homepage -> yield person ...)()
192
193 # Comprehension with result from expression:
194 homepages = (?person one foaf:homepage) each ?person a foaf:Person if ?person foaf:homepage
195
196
197 # Sets:
198 set = :set([:a, :b, :a])
199 uniqueSites = :set(hasSites)
200
201
202 # TODO: pack/unpack tuples (don't? this removes us from the rdf:iness - can't that be clean enough?)
203 first, second = %(1, 2)
204 pair = me %(first=1)
205 :print(pair%first)
206 # TODO: is this needed, or is rdf lookup enough?
207 myName, mySite, myProjects = %(me foaf:name, me foaf:homepage, [?o each me foaf:currentProject ?o])
208 myInfo = %(name=me foaf:name)
209 :print(myInfo%name)
210
211