source: ruby/trunk/lib/adl_parser/lib/adl_scanner.rb @ 265

Last change on this file since 265 was 265, checked in by Tatsukawa, Akimichi, 11 years ago

refactoring adl scanners in object-oriented way in progress

File size: 48.2 KB
Line 
1require 'rubygems'
2require 'yaparc'
3require 'logger'
4require 'adl_parser.rb'
5require 'am.rb'
6require 'rm.rb'
7
8
9module OpenEHR
10  module ADL
11    module Scanner
12
13      class Base
14        def initialize(adl_type, filename, lineno = 1)
15          @adl_type = adl_type
16          @filename = filename
17          @lineno = lineno
18        end
19
20        def scan(data)
21          raise
22        end
23      end
24
25      class CADLScanner < Base
26
27        @@logger = Logger.new('log/scanner.log')
28        RESERVED = {
29          'then' => :SYM_THEN, # [Tt][Hh][Ee][Nn]
30          'else' => :SYM_ELSE, # [Ee][Ll][Ss][Ee]
31          'and' => :SYM_AND, # [Aa][Nn][Dd]
32          'or' => :SYM_OR, # [Oo][Rr]
33          'xor' => :SYM_XOR, # [Xx][Oo][Rr]
34          'not' => :SYM_NOT, # [Nn][Oo][Tt]
35          'implies' => :SYM_IMPLIES, # [Ii][Mm][Pp][Ll][Ii][Ee][Ss]
36          'true' => :SYM_TRUE, #[Tt][Rr][Uu][Ee] -- -> SYM_TRUE
37          'false' => :SYM_FALSE, # [Ff][Aa][Ll][Ss][Ee] -- -> SYM_FALSE
38          'forall' => :SYM_FORALL, # [Ff][Oo][Rr][_][Aa][Ll][Ll]
39          'exists' => :SYM_EXISTS, # [Ee][Xx][Ii][Ss][Tt][Ss]
40          'existence' => :SYM_EXISTENCE, # [Ee][Xx][Iu][Ss][Tt][Ee][Nn][Cc][Ee]
41          'occurrences' => :SYM_OCCURRENCES, # [Oo][Cc][Cc][Uu][Rr][Rr][Ee][Nn][Cc][Ee][Ss]
42          'cardinality' => :SYM_CARDINALITY, # [Cc][Aa][Rr][Dd][Ii][Nn][Aa][Ll][Ii][Tt][Yy]
43          'ordered' => :SYM_ORDERED, # [Oo][Rr][Dd][Ee][Rr][Ee][Dd]
44          'unordered' => :SYM_UNORDERED, # [Uu][Nn][Oo][Rr][Dd][Ee][Rr][Ee][Dd]
45          'unique' => :SYM_UNIQUE, # [Uu][Nn][Ii][Qq][Uu][Ee]
46          'matches' => :SYM_MATCHES, # [Mm][Aa][Tt][Cc][Hh][Ee][Ss]
47          'is_in' => :SYM_MATCHES, # [Ii][Ss][_][Ii][Nn]
48          'invariant' => :SYM_INVARIANT, # [Ii][Nn][Vv][Aa][Rr][Ii][Aa][Nn][Tt]
49          'infinity' => :SYM_INFINITY, # [Ii][Nn][Ff][Ii][Nn][Ii][Tt][Yy] -- -> SYM_INFINITY
50          'use_node' => :SYM_USE_NODE, # [Uu][Ss][Ee][_][Nn][Oo][Dd][Ee]
51          'use_archetype' => :SYM_ALLOW_ARCHETYPE, # [Uu][Ss][Ee][_][Aa][Rr][Cc][Hh][Ee][Tt][Yy][Pp][Ee]
52          'allow_archetype' => :SYM_ALLOW_ARCHETYPE, # [Aa][Ll][Ll][Oo][Ww][_][Aa][Rr][Cc][Hh][Ee][Tt][Yy][Pp][Ee]
53          'include' => :SYM_INCLUDE, # [Ii][Nn][Cc][Ll][Uu][Dd][Ee]
54          'exclude' => :SYM_EXCLUDE # [Ee][Xx][Cc][Ll][Uu][Dd][Ee]
55        }
56
57        def initialize(adl_type, filename, lineno = 1)
58          super(adl_type, filename, lineno)
59          @in_interval = false
60          @cadl_root_scanner = OpenEHR::ADL::Scanner::CADL::RootScanner.new
61
62          @adl_scanner = lambda{OpenEHR::ADL::Scanner::ADLScanner.new(adl_type, filename)}
63          @dadl_scanner = lambda{OpenEHR::ADL::Scanner::DADLScanner.new(adl_type, filename)}
64          @regex_scanner = lambda{OpenEHR::ADL::Scanner::RegexScanner.new(adl_type, filename)}
65          @term_constraint_scanner = lambda{OpenEHR::ADL::Scanner::TermConstraintScanner.new(adl_type, filename)}
66        end
67
68        def scan(data)
69          @@logger.debug("#{__FILE__}:#{__LINE__}: Entering scan_cadl at #{@filename}:#{@lineno}: data = #{data.inspect}")
70          until data.nil?  do
71            case @adl_type.last
72            when :adl
73              data = @adl_scanner.call.scan(data) do |sym, val|
74                yield sym, val
75              end
76            when :dadl
77              data = @dadl_scanner.call.scan(data) do |sym, val|
78                yield sym, val
79              end
80            when :regexp
81              data = @regex_scanner.call..scan(data) do |sym, val|
82                yield sym, val
83              end
84            when :term_constraint
85              @@logger.debug("#{__FILE__}:#{__LINE__}: scan_cadl: Entering scan_term_constraint at #{@filename}:#{@lineno}: data = #{data.inspect}")
86             
87              data = term_constraint_scanner.scan(data) do |sym, val|
88                yield sym, val
89              end
90            when :cadl
91#               case scanned = @cadl_root_scanner.parse(data)
92#               when Yaparc::Result::OK
93#                 if scanned.value[0] == :START_V_C_DOMAIN_TYPE_BLOCK
94#                   @in_c_domain_type = true
95#                   @adl_type.push(:dadl)
96#                   yield scanned.value
97#                 else
98#                   yield scanned.value
99#                 end
100#                 data = scanned.input
101#               end
102
103              case data
104              when /\A\n/ # carriage return
105                @lineno += 1
106                ;
107              when /\A[ \t\r\f]+/ #just drop it
108                ;
109              when /\A--.*\n/ # single line comment
110                @lineno += 1
111                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_cadl: COMMENT = #{$&} at #{@filename}:#{@lineno}")
112                ;
113                ###----------/* symbols */ -------------------------------------------------
114              when /\A\=/   # =
115                yield :SYM_EQ, :SYM_EQ
116              when /\A\>=/   # >=
117                yield :SYM_GE, :SYM_GE
118              when /\A\<=/   # <=
119                yield :SYM_LE, :SYM_LE
120              when /\A\</   # <
121                if @in_interval
122                  yield :SYM_LT, :SYM_LT
123                else
124                  @adl_type.push(:dadl)
125                  yield :SYM_START_DBLOCK,  $&
126                end
127              when /\A\>/   # >
128                if @in_interval
129                  yield :SYM_GT, :SYM_GT
130                else
131                  adl_type = @adl_type.pop
132                  assert_at(__FILE__,__LINE__){adl_type == :dadl}
133                  yield :SYM_END_DBLOCK, :SYM_END_DBLOCK
134                end
135              when /\A\-/   # -
136                yield :Minus_code, :Minus_code
137              when /\A\+/   # +
138                yield :Plus_code, :Plus_code
139              when /\A\*/   # *
140                yield :Star_code, :Star_code
141              when /\A\//   # /
142                yield :Slash_code, :Slash_code
143              when /\A\^/   # ^
144                yield :Caret_code, :Caret_code
145              when /\A\.\.\./   # ...
146                yield :SYM_LIST_CONTINUE, :SYM_LIST_CONTINUE
147              when /\A\.\./   # ..
148                yield :SYM_ELLIPSIS, :SYM_ELLIPSIS
149              when /\A\./   # .
150                yield :Dot_code, :Dot_code
151              when /\A\;/   # ;
152                yield :Semicolon_code, :Semicolon_code
153              when /\A\,/   # ,
154                yield :Comma_code, :Comma_code
155              when /\A\:/   # :
156                yield :Colon_code, :Colon_code
157              when /\A\!/   # !
158                yield :Exclamation_code, :Exclamation_code
159              when /\A\(/   # (
160                yield :Left_parenthesis_code, :Left_parenthesis_code
161              when /\A\)/   # )
162                yield :Right_parenthesis_code, :Right_parenthesis_code
163              when /\A\{\// #V_REGEXP
164                if @adl_type.last != :regexp
165                  @in_regexp = true
166                  @adl_type.push(:regexp)
167                  yield :START_REGEXP_BLOCK, :START_REGEXP_BLOCK
168                else
169                  raise
170                end
171                #        yield :V_REGEXP, :V_REGEXP
172              when /\A\{/   # {
173                @adl_type.push(:cadl)
174                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_cadl: entering cADL at #{@filename}:#{@lineno}")
175                yield :SYM_START_CBLOCK, :SYM_START_CBLOCK
176              when /\A\}/   # }
177                adl_type = @adl_type.pop
178                #        puts "Escaping #{adl_type}"
179                assert_at(__FILE__,__LINE__){adl_type == :cadl}
180                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_cadl: exiting cADL at #{@filename}:#{@lineno}")
181                yield :SYM_END_CBLOCK, :SYM_END_CBLOCK
182              when /\A\$/   # $
183                yield :Dollar_code, :Dollar_code
184              when /\A\?\?/   # ??
185                yield :SYM_DT_UNKNOWN, :SYM_DT_UNKNOWN
186              when /\A\?/   # ?
187                yield :Question_mark_code, :Question_mark_code
188              when /\A\|/   # |
189                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_cadl: @in_interval = #{@in_interval} at #{@filename}:#{@lineno}")
190                if @in_interval
191                  @in_interval = false
192                else
193                  #          @in_interval = false
194                  @in_interval = true
195                end
196                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_cadl: SYM_INTERVAL_DELIM at #{@filename}:#{@lineno}")
197                yield :SYM_INTERVAL_DELIM, :SYM_INTERVAL_DELIM
198
199              when /\A\[[a-zA-Z0-9()\._-]+::[a-zA-Z0-9\._-]+\]/  #V_QUALIFIED_TERM_CODE_REF form [ICD10AM(1998)::F23]
200                #      when /\A\[[a-zA-Z0-9._\-]+::[a-zA-Z0-9._\-]+\]/   #V_QUALIFIED_TERM_CODE_REF form [ICD10AM(1998)::F23]
201                yield :V_QUALIFIED_TERM_CODE_REF, $&
202              when /\A\[[a-zA-Z0-9._\- ]+::[a-zA-Z0-9._\- ]+\]/   #ERR_V_QUALIFIED_TERM_CODE_REF
203                yield :ERR_V_QUALIFIED_TERM_CODE_REF, $&
204              when /\A\[([a-zA-Z0-9\(\)\._\-]+)::[ \t\n]*/
205                @adl_type.push(:term_constraint)
206                yield :START_TERM_CODE_CONSTRAINT, $1
207              when /\A\[[a-zA-Z0-9][a-zA-Z0-9._\-]*\]/   #V_LOCAL_TERM_CODE_REF
208                yield :V_LOCAL_TERM_CODE_REF, $&
209              when /\A\[/   # [
210                yield :Left_bracket_code, :Left_bracket_code
211              when /\A\]/   # ]
212                yield :Right_bracket_code, :Right_bracket_code
213              when /\A[A-Z][a-zA-Z0-9_]*<[a-zA-Z0-9,_<>]+>/   #V_GENERIC_TYPE_IDENTIFIER
214                yield :V_GENERIC_TYPE_IDENTIFIER, $&
215              when /\A[yY][yY][yY][yY]-[mM?X][mM?X]-[dD?X][dD?X][T\t][hH?X][hH?X]:[mM?X][mM?X]:[sS?X][sS?X]/
216                yield :V_ISO8601_DATE_TIME_CONSTRAINT_PATTERN, $&
217              when /\A[yY][yY][yY][yY]-[mM?X][mM?X]-[dD?X][dD?X]/
218                yield :V_ISO8601_DATE_CONSTRAINT_PATTERN, $&
219              when /\A[hH][hH]:[mM?X][mM?X]:[sS?X][sS?X]/
220                yield :V_ISO8601_TIME_CONSTRAINT_PATTERN, $&
221              when /\A[a-z][a-zA-Z0-9_]*/
222                word = $&.dup
223                if RESERVED[word.downcase]
224                  yield RESERVED[word.downcase], RESERVED[word.downcase]
225                else
226                  @@logger.debug("#{__FILE__}:#{__LINE__}: scan_cadl: V_ATTRIBUTE_IDENTIFIER = #{word} at #{@filename}:#{@lineno}")
227                  yield :V_ATTRIBUTE_IDENTIFIER, word #V_ATTRIBUTE_IDENTIFIER /\A[a-z][a-zA-Z0-9_]*/
228                end
229              when /\A[A-Z][a-zA-Z0-9_]*/
230                word = $&.dup
231                if RESERVED[word.downcase]
232                  yield RESERVED[word.downcase], RESERVED[word.downcase]
233                else
234                  yield :V_TYPE_IDENTIFIER, $&
235                end
236              when /\Aa[ct][0-9.]+/   #V_LOCAL_CODE
237                yield :V_LOCAL_CODE, $&
238              when /\A[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9]:[0-6][0-9](,[0-9]+)?(Z|[+-][0-9]{4})?|[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9](Z|[+-][0-9]{4})?|[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9](Z|[+-][0-9]{4})?/   #V_ISO8601_EXTENDED_DATE_TIME YYYY-MM-DDThh:mm:ss[,sss][Z|+/- -n-n-n-n-]-
239                yield :V_ISO8601_EXTENDED_DATE_TIME, $&
240              when /\A[0-2][0-9]:[0-6][0-9]:[0-6][0-9](,[0-9]+)?(Z|[+-][0-9]{4})?|[0-2][0-9]:[0-6][0-9](Z|[+-][0-9]{4})? /   #V_ISO8601_EXTENDED_TIME hh:mm:ss[,sss][Z|+/-nnnn]
241                yield :V_ISO8601_EXTENDED_TIME, $&
242              when /\A[0-9]{4}-[0-1][0-9]-[0-3][0-9]|[0-9]{4}-[0-1][0-9]/   #V_ISO8601_EXTENDED_DATE YYYY-MM-DD
243                yield :V_ISO8601_EXTENDED_DATE, $&
244              when /\A[0-9]+|[0-9]+[eE][+-]?[0-9]+/   #V_INTEGER
245                yield :V_INTEGER, $&
246              when /\A[0-9]+\.[0-9]+|[0-9]+\.[0-9]+[eE][+-]?[0-9]+ /   #V_REAL
247                yield :V_REAL, $&
248              when /\A"((?:[^"\\]+|\\.)*)"/ #V_STRING
249              when /\A"([^"]*)"/m #V_STRING
250                yield :V_STRING, $1
251              when /\A[a-z]+:\/\/[^<>|\\{}^~"\[\] ]*/ #V_URI
252                yield :V_URI, $&
253              when /\A\S/ #UTF8CHAR
254                yield :UTF8CHAR, $&
255              else
256                raise
257              end
258              data = $' # variable $' receives the string after the match
259            else
260              raise
261            end
262          end # of until
263        end
264      end # of
265
266      class DADLScanner < Base
267        @@logger = Logger.new('log/scanner.log')
268        RESERVED = {
269          'true' => :SYM_TRUE, #[Tt][Rr][Uu][Ee] -- -> SYM_TRUE
270          'false' => :SYM_FALSE, # [Ff][Aa][Ll][Ss][Ee] -- -> SYM_FALSE
271          'infinity' => :SYM_INFINITY # [Ii][Nn][Ff][Ii][Nn][Ii][Tt][Yy] -- -> SYM_INFINITY
272        }
273
274        def initialize(adl_type, filename, lineno = 1)
275          super(adl_type, filename, lineno)
276          @dadl_root_scanner = OpenEHR::ADL::Scanner::DADL::RootScanner.new
277          @in_c_domain_type = false
278
279          @adl_scanner = lambda{OpenEHR::ADL::Scanner::ADLScanner.new(adl_type, filename)}
280          @cadl_scanner = lambda{OpenEHR::ADL::Scanner::CADLScanner.new(adl_type, filename)}
281          @regex_scanner = lambda{OpenEHR::ADL::Scanner::RegexScanner.new(adl_type, filename)}
282          @term_constraint_scanner = lambda{OpenEHR::ADL::Scanner::TermConstraintScanner.new(adl_type, filename)}
283        end
284
285
286        def scan(data)
287          @@logger.debug("#{__FILE__}:#{__LINE__}: Entering scan_dadl at #{@filename}:#{@lineno}: data = #{data.inspect}")
288          until data.nil?  do
289            case @adl_type.last
290            when :adl
291              data = @adl_scanner.call.scan(data) do |sym, val|
292                yield sym, val
293              end
294            when :cadl
295              data = @cadl_scanner.call.scan(data) do |sym, val|
296                yield sym, val
297              end
298            when :regexp
299              data = @regex_scanner.call.scan(data) do |sym, val|
300                yield sym, val
301              end
302            when :term_constraint
303              @@logger.debug("#{__FILE__}:#{__LINE__}: scan_dadl: Entering scan_term_constraint at #{@filename}:#{@lineno}: data = #{data.inspect}")
304
305              data = @term_constraint_scanner.call.scan(data) do |sym, val|
306                yield sym, val
307              end
308            when :dadl
309#               case scanned = @dadl_root_scanner.parse(data)
310#               when Yaparc::Result::OK
311#                 yield scanned.value
312#                 data = scanned.input
313#               else
314#               end
315
316              case data
317              when /\A\n/ # carriage return
318                @lineno += 1
319                ;
320              when /\A[ \t\r\f]+/ #just drop it
321                ;
322              when /\A--.*\n/ # single line comment
323                @lineno += 1
324                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_dadl: COMMENT = #{$&} at #{@filename}:#{@lineno}")
325                ;
326                ###----------/* symbols */ -------------------------------------------------
327              when /\A\=/   # =
328                yield :SYM_EQ, :SYM_EQ
329              when /\A\>\=/   # >=
330                yield :SYM_GE, :SYM_GE
331              when /\A\<\=/   # <=
332                yield :SYM_LE, :SYM_LE
333              when /\A\</   # <
334                if @in_interval
335                  yield :SYM_LT, :SYM_LT
336                else
337                  @adl_type.push(:dadl)
338                  yield :SYM_START_DBLOCK, :SYM_START_DBLOCK
339                end
340              when /\A\>/   # >
341                if @in_interval
342                  yield :SYM_GT, :SYM_GT
343                elsif @in_c_domain_type == true
344                  assert_at(__FILE__,__LINE__){@adl_type.last == :dadl}
345                  adl_type = @adl_type.pop
346                  if @adl_type.last == :cadl
347                    @in_c_domain_type = false
348                    yield :END_V_C_DOMAIN_TYPE_BLOCK, $&
349                  else
350                    yield :SYM_END_DBLOCK, $&
351                  end
352                elsif @in_c_domain_type == false
353                  adl_type = @adl_type.pop
354                  assert_at(__FILE__,__LINE__){adl_type == :dadl}
355                  yield :SYM_END_DBLOCK, $&
356                else
357                  raise
358                end
359              when /\A\-/   # -
360                yield :Minus_code, :Minus_code
361              when /\A\+/   # +
362                yield :Plus_code, :Plus_code
363              when /\A\*/   # *
364                yield :Star_code, :Star_code
365              when /\A\//   # /
366                yield :Slash_code, :Slash_code
367              when /\A\^/   # ^
368                yield :Caret_code, :Caret_code
369              when /\A\.\.\./   # ...
370                yield :SYM_LIST_CONTINUE, :SYM_LIST_CONTINUE
371              when /\A\.\./   # ..
372                yield :SYM_ELLIPSIS, :SYM_ELLIPSIS
373              when /\A\./   # .
374                yield :Dot_code, :Dot_code
375              when /\A\;/   # ;
376                yield :Semicolon_code, :Semicolon_code
377              when /\A\,/   # ,
378                yield :Comma_code, :Comma_code
379              when /\A\:/   # :
380                yield :Colon_code, :Colon_code
381              when /\A\!/   # !
382                yield :Exclamation_code, :Exclamation_code
383              when /\A\(/   # (
384                yield :Left_parenthesis_code, :Left_parenthesis_code
385              when /\A\)/   # )
386                yield :Right_parenthesis_code, :Right_parenthesis_code
387              when /\A\$/   # $
388                yield :Dollar_code, :Dollar_code
389              when /\A\?\?/   # ??
390                yield :SYM_DT_UNKNOWN, :SYM_DT_UNKNOWN
391              when /\A\?/   # ?
392                yield :Question_mark_code, :Question_mark_code
393              when /\A\|/   # |
394                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_dadl: @in_interval = #{@in_interval} at #{@filename}:#{@lineno}")
395                if @in_interval
396                  @in_interval = false
397                else
398                  #          @in_interval = false
399                  @in_interval = true
400                end
401                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_dadl: SYM_INTERVAL_DELIM at #{@filename}:#{@lineno}")
402                yield :SYM_INTERVAL_DELIM, :SYM_INTERVAL_DELIM
403              when /\A\[/   # [
404                yield :Left_bracket_code, :Left_bracket_code
405              when /\A\]/   # ]
406                yield :Right_bracket_code, :Right_bracket_code
407              when /\A[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9]:[0-6][0-9](,[0-9]+)?(Z|[+-][0-9]{4})?|[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9](Z|[+-][0-9]{4})?|[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9](Z|[+-][0-9]{4})?/   #V_ISO8601_EXTENDED_DATE_TIME YYYY-MM-DDThh:mm:ss[,sss][Z|+/- -n-n-n-n-]-
408                yield :V_ISO8601_EXTENDED_DATE_TIME, $&
409              when /\A[0-2][0-9]:[0-6][0-9]:[0-6][0-9](,[0-9]+)?(Z|[+-][0-9]{4})?|[0-2][0-9]:[0-6][0-9](Z|[+-][0-9]{4})? /   #V_ISO8601_EXTENDED_TIME hh:mm:ss[,sss][Z|+/-nnnn]
410                yield :V_ISO8601_EXTENDED_TIME, $&
411              when /\A[0-9]{4}-[0-1][0-9]-[0-3][0-9]|[0-9]{4}-[0-1][0-9]/   #V_ISO8601_EXTENDED_DATE YYYY-MM-DD
412                yield :V_ISO8601_EXTENDED_DATE, $&
413              when /\A[A-Z][a-zA-Z0-9_]*<[a-zA-Z0-9,_<>]+>/   #V_GENERIC_TYPE_IDENTIFIER
414                yield :V_GENERIC_TYPE_IDENTIFIER, $&
415              when /\A[0-9]+|[0-9]+[eE][+-]?[0-9]+/   #V_INTEGER
416                yield :V_INTEGER, $&
417              when /\A[a-z]+:\/\/[^<>|\\{}^~"\[\] ]*/ #V_URI
418                yield :V_URI, $&
419              when /\A\S/ #UTF8CHAR
420                yield :UTF8CHAR, $&
421              end
422              data = $' # variable $' receives the string after the match
423            else
424              raise
425            end
426          end
427        end
428      end
429
430      class RegexScanner < Base
431        def initialize(adl_type, filename, lineno = 1)
432          super(adl_type, filename, lineno)
433          @adl_scanner = lambda{OpenEHR::ADL::Scanner::ADLScanner.new(adl_type, filename)}
434          @cadl_scanner = lambda{OpenEHR::ADL::Scanner::CADLScanner.new(adl_type, filename)}
435          @dadl_scanner = lambda{OpenEHR::ADL::Scanner::DADLScanner.new(adl_type, filename)}
436          @term_constraint_scanner = lambda{OpenEHR::ADL::Scanner::TermConstraintScanner.new(adl_type, filename)}
437        end
438
439        def scan(data)
440          @@logger.debug("#{__FILE__}:#{__LINE__}: Entering scan_regexp at #{@filename}:#{@lineno}: data = #{data.inspect}")
441          until data.nil?  do
442            case @adl_type.last
443            when :regexp
444              case data
445              when /\A\/\}/ #V_REGEXP
446                if @adl_type.last == :regexp
447                  @in_regexp = false
448                  @adl_type.pop
449                  yield :END_REGEXP_BLOCK, :END_REGEXP_BLOCK
450                else
451                  raise
452                end
453              when /\A(.*)(\/\})/ #V_REGEXP
454                yield :REGEXP_BODY, $1
455                if @adl_type.last == :regexp
456                  @in_regexp = false
457                  @adl_type.pop
458                  yield :END_REGEXP_BLOCK, :END_REGEXP_BLOCK
459                else
460                  raise
461                end
462              else
463                raise data
464              end
465              data = $' # variable $' receives the string after the match
466            when :adl
467              data = @adl_scanner.call.scan(data) do |sym, val|
468                yield sym, val
469              end
470            when :dadl
471              data = @dadl_scanner.scan(data) do |sym, val|
472                yield sym, val
473              end
474            when :cadl
475              data = @cadl_scanner.scan(data) do |sym, val|
476                yield sym, val
477              end
478            when :term_constraint
479              @@logger.debug("#{__FILE__}:#{__LINE__}: scan_regexp: Entering scan_term_constraint at #{@filename}:#{@lineno}")
480              data = @term_constraint_scanner.scan(data) do |sym, val|
481                yield sym, val
482              end
483            else
484              raise
485            end
486          end
487        end
488      end
489
490      class TermConstraintScanner < Base
491        def initialize(adl_type, filename, lineno = 1)
492          super(adl_type, filename, lineno)
493          @adl_scanner = lambda{OpenEHR::ADL::Scanner::ADLScanner.new(adl_type, filename)}
494          @cadl_scanner = lambda{OpenEHR::ADL::Scanner::CADLScanner.new(adl_type, filename)}
495          @dadl_scanner = lambda{OpenEHR::ADL::Scanner::DADLScanner.new(adl_type, filename)}
496          @regex_scanner = lambda{OpenEHR::ADL::Scanner::RegexScanner.new(adl_type, filename)}
497        end
498
499        def scan(data)
500          @@logger.debug("#{__FILE__}:#{__LINE__}: Entering scan_term_constraint")
501          until data.nil?  do
502            case @adl_type.last
503            when :term_constraint
504              case data
505              when /\A\n/ # carriage return
506                @lineno += 1
507                ;
508              when /\A[ \t\r\f]+/ #just drop it
509                ;
510              when /\A--.*$/ # single line comment
511                @lineno += 1
512                #@@logger.debug("#{__FILE__}:#{__LINE__}: scan_term_constraint: COMMENT = #{$&} at #{@filename}:#{@lineno}")
513                ;
514              when /\A([a-zA-Z0-9\._\-])+[ \t]*,/ # match any line, with ',' termination
515                yield :TERM_CODE, $1
516              when /\A([a-zA-Z0-9\._\-])+[ \t]*;/ # match second last line with ';' termination (assumed value)
517                yield :TERM_CODE, $1
518              when /\A([a-zA-Z0-9\._\-])*[ \t]*\]/ # match final line, terminating in ']'
519                adl_type = @adl_type.pop
520                assert_at(__FILE__,__LINE__){adl_type == :term_constraint}
521                yield :END_TERM_CODE_CONSTRAINT, $1
522              else
523                raise "data = #{data}"
524              end
525              data = $' # variable $' receives the string after the match
526            when :adl
527              data = @adl_scanner.call.scan(data) do |sym, val|
528                yield sym, val
529              end
530            when :dadl
531              data = @dadl_scanner.call.scan(data) do |sym, val|
532                yield sym, val
533              end
534            when :cadl
535              data = @cadl_scanner.call.scan(data) do |sym, val|
536                yield sym, val
537              end
538            else
539              raise
540            end
541          end
542        end
543      end
544
545      class ADLScanner < Base
546        attr_accessor :adl_type, :lineno, :cadl_scanner, :dadl_scanner, :regex_scanner, :term_constraint_scanner
547
548        @@logger = Logger.new('log/scanner.log')
549        RESERVED = {
550          'archetype' => :SYM_ARCHETYPE,
551          'adl_version' => :SYM_ADL_VERSION,
552          'controlled' => :SYM_IS_CONTROLLED,
553          'specialize' => :SYM_SPECIALIZE,
554          'concept' => :SYM_CONCEPT,
555          'language' => :SYM_LANGUAGE,
556          'description' => :SYM_DESCRIPTION,
557          'definition' => :SYM_DEFINITION,
558          'invariant' => :SYM_INVARIANT,
559          'ontology' => :SYM_ONTOLOGY,
560          'matches' => :SYM_MATCHES,
561          'is_in' => :SYM_MATCHES,
562          'occurrences' => :SYM_OCCURRENCES,
563          'true' => :SYM_TRUE, #[Tt][Rr][Uu][Ee] -- -> SYM_TRUE
564          'false' => :SYM_FALSE, # [Ff][Aa][Ll][Ss][Ee] -- -> SYM_FALSE
565          'infinity' => :SYM_INFINITY # [Ii][Nn][Ff][Ii][Nn][Ii][Tt][Yy] -- -> SYM_INFINITY
566        }
567
568        def initialize(adl_type, filename, lineno = 1)
569          super(adl_type, filename, lineno)
570          @cadl_scanner = lambda{OpenEHR::ADL::Scanner::CADLScanner.new(adl_type, filename)}
571          @dadl_scanner = lambda{OpenEHR::ADL::Scanner::DADLScanner.new(adl_type, filename)}
572          @regex_scanner = lambda{OpenEHR::ADL::Scanner::RegexScanner.new(adl_type, filename)}
573          @term_constraint_scanner = lambda{OpenEHR::ADL::Scanner::TermConstraintScanner.new(adl_type, filename)}
574        end
575
576        def scan(data)
577          @@logger.debug("#{__FILE__}:#{__LINE__}: Entering scan_adl at #{@filename}:#{@lineno}: data = #{data.inspect}")
578          until data.nil?  do
579            case @adl_type.last
580            when :dadl
581              data = @dadl_scanner.call.scan(data) do |sym, val|
582                yield sym, val
583              end
584            when :cadl
585              data = @cadl_scanner.call.scan(data) do |sym, val|
586                yield sym, val
587              end
588            when :regexp
589              data = @regex_scanner.call.scan(data) do |sym, val|
590                yield sym, val
591              end
592            when :term_constraint
593              @@logger.debug("#{__FILE__}:#{__LINE__}: scan_adl: Entering scan_term_constraint at #{@filename}:#{@lineno}: data = #{data.inspect}")
594
595              data = @term_constraint_scanner.call.scan(data) do |sym, val|
596                yield sym, val
597              end
598            when :adl
599              case data
600              when /\A\n/ # carriage return
601                @lineno += 1
602                ;
603              when /\A[ \t\r\f]+/ #just drop it
604                ;
605              when /\A--.*\n/ # single line comment
606                @lineno += 1
607                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_adl: COMMENT = #{$&} at #{@filename}:#{@lineno}")
608                ;
609              when /\Adescription/   # description
610                yield :SYM_DESCRIPTION, :SYM_DESCRIPTION
611              when /\Adefinition/   # definition
612                yield :SYM_DEFINITION, :SYM_DEFINITION
613                ###----------/* symbols */ -------------------------------------------------
614              when /\A[A-Z][a-zA-Z0-9_]*/
615                yield :V_TYPE_IDENTIFIER, $&
616                #      when /\A[a-zA-Z][a-zA-Z0-9_-]+\.[a-zA-Z][a-zA-Z0-9_-]+\.[a-zA-Z0-9]+/   #V_ARCHETYPE_ID
617              when /\A(\w+)-(\w+)-(\w+)\.(\w+)(-\w+)?\.(v\w+)/   #V_ARCHETYPE_ID
618                object_id, rm_originator, rm_name, rm_entity, concept_name, specialisation, version_id = $&, $1, $2, $3, $4, $5, $6
619                archetype_id = OpenEHR::RM::Support::Identification::Archetype_ID.new(object_id, concept_name, rm_name, rm_entity, rm_originator, specialisation, version_id)
620                #        yield :V_ARCHETYPE_ID, $&
621                yield :V_ARCHETYPE_ID, archetype_id
622              when /\A[a-z][a-zA-Z0-9_]*/
623                #        word = $&.downcase
624                word = $&
625                if RESERVED[word]
626                  @@logger.debug("#{__FILE__}:#{__LINE__}: scan_adl: RESERVED = #{RESERVED[word]} at #{@filename}:#{@lineno}")
627                  yield RESERVED[word], RESERVED[word]
628                elsif #/\A[A-Z][a-zA-Z0-9_]*/
629                  @@logger.debug("#{__FILE__}:#{__LINE__}: scan_adl: V_ATTRIBUTE_IDENTIFIER = #{$&} at #{@filename}:#{@lineno}")
630                  yield :V_ATTRIBUTE_IDENTIFIER, $&
631                end
632              when /\A\=/   # =
633                yield :SYM_EQ, :SYM_EQ
634              when /\A\>=/   # >=
635                yield :SYM_GE, :SYM_GE
636              when /\A\<=/   # <=
637                yield :SYM_LE, :SYM_LE
638              when /\A\</   # <
639                if @in_interval                   # @start_block_received = false
640                  yield :SYM_LT, :SYM_LT
641                else                              # @start_block_received = true
642                  @adl_type.push(:dadl)
643                  yield :SYM_START_DBLOCK,  $&
644                end
645              when /\A\>/   # >
646                if @in_interval
647                  yield :SYM_GT, :SYM_GT
648                else
649                  adl_type = @adl_type.pop
650                  assert_at(__FILE__,__LINE__){adl_type == :dadl}
651                  yield :SYM_END_DBLOCK, :SYM_END_DBLOCK
652                end
653              when /\A\{/   # {
654                @adl_type.push(:cadl)
655                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_cadl: entering cADL at #{@filename}:#{@lineno}")
656                yield :SYM_START_CBLOCK, :SYM_START_CBLOCK
657              when /\A\}/   # }
658                adl_type = @adl_type.pop
659                assert_at(__FILE__,__LINE__){adl_type == :cadl}
660                @@logger.debug("#{__FILE__}:#{__LINE__}: scan_cadl: exiting cADL at #{@filename}:#{@lineno}")
661                yield :SYM_END_CBLOCK, $&
662              when /\A\-/   # -
663                yield :Minus_code, :Minus_code
664              when /\A\+/   # +
665                yield :Plus_code, :Plus_code
666              when /\A\*/   # *
667                yield :Star_code, :Star_code
668              when /\A\//   # /
669                yield :Slash_code, :Slash_code
670              when /\A\^/   # ^
671                yield :Caret_code, :Caret_code
672              when /\A\=/   # =
673                yield :Equal_code, :Equal_code
674              when /\A\.\.\./   # ...
675                yield :SYM_LIST_CONTINUE, :SYM_LIST_CONTINUE
676              when /\A\.\./   # ..
677                yield :SYM_ELLIPSIS, :SYM_ELLIPSIS
678              when /\A\./   # .
679                yield :Dot_code, :Dot_code
680              when /\A\;/   # ;
681                yield :Semicolon_code, :Semicolon_code
682              when /\A\,/   # ,
683                yield :Comma_code, :Comma_code
684              when /\A\:/   # :
685                yield :Colon_code, :Colon_code
686              when /\A\!/   # !
687                yield :Exclamation_code, :Exclamation_code
688              when /\A\(/   # (
689                yield :Left_parenthesis_code, :Left_parenthesis_code
690              when /\A\)/   # )
691                yield :Right_parenthesis_code, :Right_parenthesis_code
692              when /\A\$/   # $
693                yield :Dollar_code, :Dollar_code
694              when /\A\?\?/   # ??
695                yield :SYM_DT_UNKNOWN, :SYM_DT_UNKNOWN
696              when /\A\?/   # ?
697                yield :Question_mark_code, :Question_mark_code
698              when /\A[0-9]+\.[0-9]+(\.[0-9]+)*/   # ?
699                yield :V_VERSION_STRING, $&
700              when /\A\|/   # |
701                if @in_interval
702                  @in_interval = false
703                else
704                  @in_interval = true
705                end
706                yield :SYM_INTERVAL_DELIM, :SYM_INTERVAL_DELIM
707              when /\A\[[a-zA-Z0-9()\._-]+::[a-zA-Z0-9\._-]+\]/
708                #      when /\A\[[a-zA-Z0-9()\._-]+\:\:[a-zA-Z0-9\._-]+\]/   #V_QUALIFIED_TERM_CODE_REF form [ICD10AM(1998)::F23]
709                yield :V_QUALIFIED_TERM_CODE_REF, $&
710              when /\A\[[a-zA-Z0-9][a-zA-Z0-9._\-]*\]/   #V_LOCAL_TERM_CODE_REF
711                yield :V_LOCAL_TERM_CODE_REF, $&
712              when /\A\[/   # [
713                yield :Left_bracket_code, :Left_bracket_code
714              when /\A\]/   # ]
715                yield :Right_bracket_code, :Right_bracket_code
716
717              when /\A"([^"]*)"/m #V_STRING
718                yield :V_STRING, $1
719              when /\A\[[a-zA-Z0-9._\- ]+::[a-zA-Z0-9._\- ]+\]/   #ERR_V_QUALIFIED_TERM_CODE_REF
720                yield :ERR_V_QUALIFIED_TERM_CODE_REF, $&
721              when /\Aa[ct][0-9.]+/   #V_LOCAL_CODE
722                yield :V_LOCAL_CODE, $&
723              when /\A[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9]:[0-6][0-9](,[0-9]+)?(Z|[+-][0-9]{4})?|[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9](Z|[+-][0-9]{4})?|[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9](Z|[+-][0-9]{4})?/   #V_ISO8601_EXTENDED_DATE_TIME YYYY-MM-DDThh:mm:ss[,sss][Z|+/- -n-n-n-n-]-
724                yield :V_ISO8601_EXTENDED_DATE_TIME, $&
725              when /\A[0-2][0-9]:[0-6][0-9]:[0-6][0-9](,[0-9]+)?(Z|[+-][0-9]{4})?|[0-2][0-9]:[0-6][0-9](Z|[+-][0-9]{4})? /   #V_ISO8601_EXTENDED_TIME hh:mm:ss[,sss][Z|+/-nnnn]
726                yield :V_ISO8601_EXTENDED_TIME, $&
727              when /\A[0-9]{4}-[0-1][0-9]-[0-3][0-9]|[0-9]{4}-[0-1][0-9]/   #V_ISO8601_EXTENDED_DATE YYYY-MM-DD
728                yield :V_ISO8601_EXTENDED_DATE, $&
729              when /\A[A-Z][a-zA-Z0-9_]*<[a-zA-Z0-9,_<>]+>/   #V_GENERIC_TYPE_IDENTIFIER
730                yield :V_GENERIC_TYPE_IDENTIFIER, $&
731              when /\A[0-9]+|[0-9]+[eE][+-]?[0-9]+/   #V_INTEGER
732                yield :V_INTEGER, $&
733              when /\A[0-9]+\.[0-9]+|[0-9]+\.[0-9]+[eE][+-]?[0-9]+ /   #V_REAL
734                yield :V_REAL, $&
735                #    when /\A"((?:[^"\\]+|\\.)*)"/ #V_STRING
736              when /\A[a-z]+:\/\/[^<>|\\{}^~"\[\] ]*/ #V_URI
737                yield :V_URI, $&
738              when /\AP([0-9]+[yY])?([0-9]+[mM])?([0-9]+[wW])?([0-9]+[dD])?T([0-9]+[hH])?([0-9]+[mM])?([0-9]+[sS])?|P([0-9]+[yY])?([0-9]+[mM])?([0-9]+[wW])?([0-9]+[dD])?/   #V_ISO8601_DURATION PnYnMnWnDTnnHnnMnnS
739                yield :V_ISO8601_DURATION, $&
740              when /\A\S/ #UTF8CHAR
741                yield :UTF8CHAR, $&
742              end
743              data = $' # variable $' receives the string after the match
744            else
745              raise
746            end
747          end
748        end
749      end
750
751
752      module Common
753        class START_TERM_CODE_CONSTRAINT
754          include Yaparc::Parsable
755          def initialize
756            @parser = lambda do |input|
757              Yaparc::Apply.new(Yaparc::Regex.new(/[ \t\n]*\[([a-zA-Z0-9\(\)\._\-]+)::[ \t\n]*/)) do |match|
758                OpenEHR::LOG.info("START_TERM_CODE_CONSTRAINT: #{match}")
759                [:START_TERM_CODE_CONSTRAINT, match]
760              end
761            end
762          end
763        end
764
765        # /\A\[[a-zA-Z0-9()\._-]+::[a-zA-Z0-9\._-]+\]/  #V_QUALIFIED_TERM_CODE_REF form [ICD10AM(1998)::F23]
766        class V_QUALIFIED_TERM_CODE_REF
767          include Yaparc::Parsable
768          def initialize
769            @parser = lambda do |input|
770              Yaparc::Apply.new(Yaparc::Regex.new(/\A\[[a-zA-Z0-9()\._-]+::[a-zA-Z0-9\._-]+\]/)) do |match|
771                OpenEHR::LOG.info("V_QUALIFIED_TERM_CODE_REF: #{match}")
772                [:V_QUALIFIED_TERM_CODE_REF, match]
773              end
774            end
775          end
776        end
777       
778        class V_LOCAL_TERM_CODE_REF
779          include Yaparc::Parsable
780          def initialize
781            @parser = lambda do |input|
782              Yaparc::Apply.new(Yaparc::Regex.new(/\A\[[a-zA-Z0-9][a-zA-Z0-9._\-]*\]/)) do |match|
783                OpenEHR::LOG.info("V_TERM_CODE_REF: #{match}")
784                [:V_LOCAL_TERM_CODE_REF, match]
785              end
786            end
787          end
788        end
789
790        class ERR_V_QUALIFIED_TERM_CODE_REF
791          include Yaparc::Parsable
792          def initialize
793            @parser = lambda do |input|
794              Yaparc::Apply.new(Yaparc::Regex.new(/\A\[[a-zA-Z0-9._\- ]+::[a-zA-Z0-9._\- ]+\]/)) do |match|
795                OpenEHR::LOG.info("ERR_V_QUALIFIED_TERM_CODE_REF: #{match}")
796                [:ERR_V_QUALIFIED_TERM_CODE_REF, match]
797              end
798            end
799          end
800        end
801
802        class V_TYPE_IDENTIFIER
803          include Yaparc::Parsable
804          def initialize
805            @parser = lambda do |input|
806              Yaparc::Apply.new(Yaparc::Regex.new(/\A[A-Z][a-zA-Z0-9_]*/)) do |match|
807                OpenEHR::LOG.info("V_TYPE_IDENTIFIER: #{match}")
808                [:V_TYPE_IDENTIFIER, match]
809              end
810            end
811          end
812        end
813
814        class V_GENERIC_TYPE_IDENTIFIER
815          include Yaparc::Parsable
816          def initialize
817            @parser = lambda do |input|
818              Yaparc::Apply.new(Yaparc::Regex.new(/\A[A-Z][a-zA-Z0-9_]*<[a-zA-Z0-9,_<>]+>/)) do |match|
819                OpenEHR::LOG.info("V_GENERIC_TYPE_IDENTIFIER: #{match}")
820                [:V_GENERIC_TYPE_IDENTIFIER, match]
821              end
822            end
823          end
824        end
825
826
827        class V_LOCAL_CODE
828          include Yaparc::Parsable
829          def initialize
830            @parser = lambda do |input|
831              Yaparc::Apply.new(Yaparc::Regex.new(/\Aa[ct][0-9.]+/)) do |match|
832                OpenEHR::LOG.info("V_LOCAL_CODE: #{match}")
833                [:V_LOCAL_CODE, match]
834              end
835            end
836          end
837        end
838
839        class V_STRING
840          include Yaparc::Parsable
841          def initialize
842            @parser = lambda do |input|
843              Yaparc::Apply.new(Yaparc::Regex.new(/\A"([^"]*)"/m)) do |match|
844                OpenEHR::LOG.info("V_STRING: #{match}")
845                [:V_STRING, match]
846              end
847            end
848          end
849        end
850
851        class V_REAL
852          include Yaparc::Parsable
853          def initialize
854            @parser = lambda do |input|
855              Yaparc::Apply.new(Yaparc::Regex.new(/\A[0-9]+\.[0-9]+|[0-9]+\.[0-9]+[eE][+-]?[0-9]+/)) do |match|
856                OpenEHR::LOG.info("V_REAL: #{match}")
857                [:V_REAL, match]
858              end
859            end
860          end
861        end
862
863        #V_ISO8601_DURATION PnYnMnWnDTnnHnnMnnS
864        class V_ISO8601_DURATION
865          include Yaparc::Parsable
866          def initialize
867            @parser = lambda do |input|
868              Yaparc::Apply.new(
869                                Yaparc::Alt.new(Yaparc::Regex.new(/\AP([0-9]+|[yY])?([0-9]+|[mM])?([0-9]+|[wW])?([0-9]+|[dD])?T([0-9]+|[hH])?([0-9]+|[mM])?([0-9]+|[sS])?/),
870                                                Yaparc::Regex.new(/\AP([0-9]+|[yY])?([0-9]+|[mM])?([0-9]+|[wW])?([0-9]+|[dD])?/))) do |match|
871                OpenEHR::LOG.info("V_ISO8601_DURATION: #{match}")
872                [:V_ISO8601_DURATION, match]
873              end
874            end
875          end
876        end
877
878      end # of Common
879
880      module DADL
881        # c.f. http://www.openehr.org/svn/ref_impl_eiffel/TRUNK/components/adl_parser/src/syntax/adl/parser/adl_scanner.l
882        RESERVED = {
883          'true' => :SYM_TRUE, #[Tt][Rr][Uu][Ee] -- -> SYM_TRUE
884          'false' => :SYM_FALSE, # [Ff][Aa][Ll][Ss][Ee] -- -> SYM_FALSE
885          'infinity' => :SYM_INFINITY # [Ii][Nn][Ff][Ii][Nn][Ii][Tt][Yy] -- -> SYM_INFINITY
886        }
887        #
888        # DADL::RootScanner
889        #
890        class RootScanner
891          include Yaparc::Parsable
892          def initialize
893            @parser = lambda do |input|
894              Yaparc::Alt.new(Reserved.new,
895                              OpenEHR::ADL::Scanner::Common::V_QUALIFIED_TERM_CODE_REF.new,
896                              OpenEHR::ADL::Scanner::Common::V_LOCAL_TERM_CODE_REF.new,
897                              OpenEHR::ADL::Scanner::Common::ERR_V_QUALIFIED_TERM_CODE_REF.new,
898                              OpenEHR::ADL::Scanner::Common::V_TYPE_IDENTIFIER.new,
899                              OpenEHR::ADL::Scanner::Common::V_GENERIC_TYPE_IDENTIFIER.new,
900                              OpenEHR::ADL::Scanner::Common::V_STRING.new,
901                              OpenEHR::ADL::Scanner::Common::V_LOCAL_CODE.new,
902                              OpenEHR::ADL::Scanner::Common::V_REAL.new,
903                              OpenEHR::ADL::Scanner::Common::V_ISO8601_DURATION.new#,
904                              #OpenEHR::ADL::Scanner::Common::START_TERM_CODE_CONSTRAINT.new
905                              )
906            end
907          end
908        end
909
910        #  <DADL::Reserved class>
911        class Reserved
912          include Yaparc::Parsable
913         
914          def initialize
915            @parser = lambda do |input|
916              reserved_parsers = OpenEHR::ADL::Scanner::DADL::RESERVED.map do |keyword| 
917                Yaparc::Tokenize.new(
918                                     Yaparc::Literal.new(keyword[0],false)
919                                     )
920              end
921              Yaparc::Alt.new(Yaparc::Apply.new(Yaparc::Alt.new(*reserved_parsers)) do |match|
922                                OpenEHR::LOG.info("Reserved: #{match}")
923                                [OpenEHR::ADL::Scanner::DADL::RESERVED[match], OpenEHR::ADL::Scanner::DADL::RESERVED[match]]
924                              end,
925                              Yaparc::Apply.new(Yaparc::Regex.new(/\A[a-z][a-zA-Z0-9_]*/)) do |match|
926                                OpenEHR::LOG.info("V_ATTRIBUTE_IDENTIFIER: #{match}")
927                                [:V_ATTRIBUTE_IDENTIFIER, match]
928                              end)
929            end
930          end
931        end
932      end # of DADL
933
934      module CADL
935        # c.f. http://www.openehr.org/svn/ref_impl_eiffel/TRUNK/components/adl_parser/src/syntax/cadl/parser/cadl_scanner.l
936        RESERVED = {
937          'ordered' => :SYM_ORDERED, # [Oo][Rr][Dd][Ee][Rr][Ee][Dd]
938          'unordered' => :SYM_UNORDERED, # [Uu][Nn][Oo][Rr][Dd][Ee][Rr][Ee][Dd]
939          'then' => :SYM_THEN, # [Tt][Hh][Ee][Nn]
940          'else' => :SYM_ELSE, # [Ee][Ll][Ss][Ee]
941          'and' => :SYM_AND, # [Aa][Nn][Dd]
942          'or' => :SYM_OR, # [Oo][Rr]
943          'xor' => :SYM_XOR, # [Xx][Oo][Rr]
944          'not' => :SYM_NOT, # [Nn][Oo][Tt]
945          'implies' => :SYM_IMPLIES, # [Ii][Mm][Pp][Ll][Ii][Ee][Ss]
946          'true' => :SYM_TRUE, #[Tt][Rr][Uu][Ee] -- -> SYM_TRUE
947          'false' => :SYM_FALSE, # [Ff][Aa][Ll][Ss][Ee] -- -> SYM_FALSE
948          'forall' => :SYM_FORALL, # [Ff][Oo][Rr][_][Aa][Ll][Ll]
949          'exists' => :SYM_EXISTS, # [Ee][Xx][Ii][Ss][Tt][Ss]
950          'existence' => :SYM_EXISTENCE, # [Ee][Xx][Iu][Ss][Tt][Ee][Nn][Cc][Ee]
951          'occurrences' => :SYM_OCCURRENCES, # [Oo][Cc][Cc][Uu][Rr][Rr][Ee][Nn][Cc][Ee][Ss]
952          'cardinality' => :SYM_CARDINALITY, # [Cc][Aa][Rr][Dd][Ii][Nn][Aa][Ll][Ii][Tt][Yy]
953          'unique' => :SYM_UNIQUE, # [Uu][Nn][Ii][Qq][Uu][Ee]
954          'matches' => :SYM_MATCHES, # [Mm][Aa][Tt][Cc][Hh][Ee][Ss]
955          'is_in' => :SYM_MATCHES, # [Ii][Ss][_][Ii][Nn]
956          'invariant' => :SYM_INVARIANT, # [Ii][Nn][Vv][Aa][Rr][Ii][Aa][Nn][Tt]
957          'infinity' => :SYM_INFINITY, # [Ii][Nn][Ff][Ii][Nn][Ii][Tt][Yy] -- -> SYM_INFINITY
958          'use_node' => :SYM_USE_NODE, # [Uu][Ss][Ee][_][Nn][Oo][Dd][Ee]
959          'use_archetype' => :SYM_ALLOW_ARCHETYPE, # [Uu][Ss][Ee][_][Aa][Rr][Cc][Hh][Ee][Tt][Yy][Pp][Ee]
960          'allow_archetype' => :SYM_ALLOW_ARCHETYPE, # [Aa][Ll][Ll][Oo][Ww][_][Aa][Rr][Cc][Hh][Ee][Tt][Yy][Pp][Ee]
961          'include' => :SYM_INCLUDE, # [Ii][Nn][Cc][Ll][Uu][Dd][Ee]
962          'exclude' => :SYM_EXCLUDE # [Ee][Xx][Cc][Ll][Uu][Dd][Ee]
963        }
964
965        #V_ISO8601_DATE_TIME_CONSTRAINT_PATTERN, /\A[yY][yY][yY][yY]-[mM?X][mM?X]-[dD?X][dD?X][T\t][hH?X][hH?X]:[mM?X][mM?X]:[sS?X][sS?X]/
966        class V_ISO8601_DATE_TIME_CONSTRAINT_PATTERN
967          include Yaparc::Parsable
968          def initialize
969            @parser = lambda do |input|
970              Yaparc::Apply.new(Yaparc::Regex.new(/\A[yY][yY][yY][yY]-[mM?X][mM?X]-[dD?X][dD?X][T\t][hH?X][hH?X]:[mM?X][mM?X]:[sS?X][sS?X]/)) do |match|
971                OpenEHR::LOG.info("V_ISO8601_DATE_TIME_CONSTRAINT_PATTERN: #{match}")
972                [:V_ISO8601_DATE_TIME_CONSTRAINT_PATTERN, match]
973              end
974            end
975          end
976        end
977
978        #V_ISO8601_DATE_CONSTRAINT_PATTERN  /\A[yY][yY][yY][yY]-[mM?X][mM?X]-[dD?X][dD?X]/
979        class V_ISO8601_DATE_CONSTRAINT_PATTERN
980          include Yaparc::Parsable
981          def initialize
982            @parser = lambda do |input|
983              Yaparc::Apply.new(Yaparc::Regex.new(/\A[yY][yY][yY][yY]-[mM?X][mM?X]-[dD?X][dD?X]/)) do |match|
984                OpenEHR::LOG.info("V_ISO8601_DATE_CONSTRAINT_PATTERN: #{match}")
985                [:V_ISO8601_DATE_CONSTRAINT_PATTERN, match]
986              end
987            end
988          end
989        end
990
991        #V_ISO8601_TIME_CONSTRAINT_PATTERN /\A[hH][hH]:[mM?X][mM?X]:[sS?X][sS?X]/
992        class V_ISO8601_TIME_CONSTRAINT_PATTERN
993          include Yaparc::Parsable
994          def initialize
995            @parser = lambda do |input|
996              Yaparc::Apply.new(Yaparc::Regex.new(/\A[hH][hH]:[mM?X][mM?X]:[sS?X][sS?X]/)) do |match|
997                OpenEHR::LOG.info("V_ISO8601_TIME_CONSTRAINT_PATTERN: #{match}")
998                [:V_ISO8601_TIME_CONSTRAINT_PATTERN, match]
999              end
1000            end
1001          end
1002        end
1003
1004        #V_ISO8601_DURATION_CONSTRAINT_PATTERN
1005        class V_ISO8601_DURATION_CONSTRAINT_PATTERN
1006          include Yaparc::Parsable
1007          def initialize
1008            @parser = lambda do |input|
1009              Yaparc::Apply.new(Yaparc::Alt.new(Yaparc::Regex.new(/\AP[yY]?[mM]?[wW]?[dD]?T[hH]?[mM]?[sS]?/),
1010                                                Yaparc::Regex.new(/\AP[yY]?[mM]?[wW]?[dD]?/))) do |match|
1011                OpenEHR::LOG.info("V_ISO8601_DURATION_CONSTRAINT_PATTERN: #{match}")
1012                [:V_ISO8601_DURATION_CONSTRAINT_PATTERN, match]
1013              end
1014            end
1015          end
1016        end
1017
1018        #V_C_DOMAIN_TYPE /\A[A-Z][a-zA-Z0-9_]*[ \n]*\</
1019        class V_C_DOMAIN_TYPE
1020          include Yaparc::Parsable
1021          def initialize
1022            @parser = lambda do |input|
1023              Yaparc::Apply.new(Yaparc::Regex.new(/\A[A-Z][a-zA-Z0-9_]*[ \n]*\</)) do |match|
1024                OpenEHR::LOG.info("V_C_DOMAIN_TYPE: #{match}")
1025                [:START_V_C_DOMAIN_TYPE_BLOCK, match]
1026              end
1027            end
1028          end
1029        end
1030
1031        #
1032        # CADL::RootScanner
1033        #
1034        class RootScanner
1035          include Yaparc::Parsable
1036          def initialize
1037            @parser = lambda do |input|
1038              Yaparc::Alt.new(V_ISO8601_DATE_TIME_CONSTRAINT_PATTERN.new,
1039                              V_ISO8601_DATE_CONSTRAINT_PATTERN.new,
1040                              V_ISO8601_TIME_CONSTRAINT_PATTERN.new,
1041                              OpenEHR::ADL::Scanner::Common::V_ISO8601_DURATION.new,
1042                              V_C_DOMAIN_TYPE.new,
1043                              V_ISO8601_DURATION_CONSTRAINT_PATTERN.new,
1044                              Reserved.new,
1045                              OpenEHR::ADL::Scanner::Common::V_QUALIFIED_TERM_CODE_REF.new,
1046                              OpenEHR::ADL::Scanner::Common::V_LOCAL_TERM_CODE_REF.new,
1047                              OpenEHR::ADL::Scanner::Common::ERR_V_QUALIFIED_TERM_CODE_REF.new,
1048                              OpenEHR::ADL::Scanner::Common::V_TYPE_IDENTIFIER.new,
1049                              OpenEHR::ADL::Scanner::Common::V_GENERIC_TYPE_IDENTIFIER.new,
1050                              OpenEHR::ADL::Scanner::Common::V_STRING.new,
1051                              OpenEHR::ADL::Scanner::Common::V_LOCAL_CODE.new,
1052                              OpenEHR::ADL::Scanner::Common::V_REAL.new,
1053                              OpenEHR::ADL::Scanner::Common::V_ISO8601_DURATION.new#,
1054                              #OpenEHR::ADL::Scanner::Common::START_TERM_CODE_CONSTRAINT.new
1055                              )
1056            end
1057          end
1058        end
1059
1060        # <CADL::Reserved class>
1061        class Reserved
1062          include Yaparc::Parsable
1063         
1064          def initialize
1065            @parser = lambda do |input|
1066              orderd_reserved = RESERVED.keys.sort{|x,y| y.length <=> x.length  }
1067              reserved_parsers = orderd_reserved.map do |keyword| 
1068                Yaparc::Literal.new(keyword,false)
1069              end
1070              Yaparc::Alt.new(Yaparc::Apply.new(Yaparc::Alt.new(*reserved_parsers)) do |match|
1071                                OpenEHR::LOG.info("Reserved: #{match}")
1072                                [OpenEHR::ADL::Scanner::CADL::RESERVED[match], OpenEHR::ADL::Scanner::CADL::RESERVED[match]]
1073                              end,
1074                              Yaparc::Apply.new(Yaparc::Regex.new(/\A[a-z][a-zA-Z0-9_]*/)) do |match|
1075                                OpenEHR::LOG.info("V_ATTRIBUTE_IDENTIFIER: #{match}")
1076                                [:V_ATTRIBUTE_IDENTIFIER, match]
1077                              end)
1078            end
1079          end
1080        end
1081
1082      end
1083    end
1084  end
1085end
Note: See TracBrowser for help on using the repository browser.