%{ indexing component: "openEHR Archetype Project" description: "Validating parser for Archetype Description Language (ADL)" keywords: "ADL, dADL" author: "Thomas Beale" support: "Ocean Informatics " copyright: "Copyright (c) 2004, 2005 Ocean Informatics Pty Ltd" license: "See notice at bottom of class" file: "$URL$" revision: "$LastChangedRevision$" last_change: "$LastChangedDate$" class DADL2_VALIDATOR inherit YY_PARSER_SKELETON rename make as make_parser_skeleton redefine report_error end DATE_TIME_ROUTINES export {NONE} all end DADL_SCANNER rename make as make_scanner, reset as reset_scanner end KL_SHARED_EXCEPTIONS KL_SHARED_ARGUMENTS create make %} %token V_INTEGER %token V_REAL %token V_TYPE_IDENTIFIER V_GENERIC_TYPE_IDENTIFIER V_ATTRIBUTE_IDENTIFIER V_STRING %token V_ISO8601_EXTENDED_DATE V_ISO8601_EXTENDED_TIME V_ISO8601_EXTENDED_DATE_TIME V_ISO8601_DURATION %token V_CADL_BLOCK %token V_LOCAL_CODE V_QUALIFIED_TERM_CODE_REF V_LOCAL_TERM_CODE_REF %token V_URI %token V_CHARACTER %token SYM_START_DBLOCK SYM_END_DBLOCK -- value block %token SYM_EQ SYM_ELLIPSIS SYM_LIST_CONTINUE SYM_INFINITY SYM_INTERVAL_DELIM %token SYM_DT_UNKNOWN -- FIXME: query syntax is obsolete from 01/jun/2005; remove when backward compatibility no longer needed %token SYM_QUERY_FUNC %token SYM_TRUE SYM_FALSE %left SYM_LT SYM_GT SYM_LE SYM_GE %token ERR_CHARACTER ERR_STRING %token ERR_V_QUALIFIED_TERM_CODE_REF %type type_identifier %type integer_value %type real_value %type boolean_value %type character_value %type date_value %type date_time_value %type time_value %type duration_value %type term_code %type uri_value %type single_attr_object_block, untyped_single_attr_object_block %type multiple_attr_object_block, untyped_multiple_attr_object_block %type complex_object_block %type untyped_primitive_object_block, primitive_object_block %type string_list_value %type integer_list_value %type real_list_value %type character_list_value %type boolean_list_value %type date_list_value %type time_list_value %type date_time_list_value %type duration_list_value %type term_code_list_value %type simple_list_value %type integer_interval_value %type real_interval_value %type time_interval_value %type date_interval_value %type date_time_interval_value %type duration_interval_value %type simple_interval_value %% input: attr_vals -- anonymous object { output := complex_object_nodes.item debug("dADL_parse") io.put_string("Object data definition validated (non-delimited)%N") end accept } | complex_object_block { output := $1 debug("dADL_parse") io.put_string("Object data definition validated%N") end accept } | error { debug("dADL_parse") io.put_string("dADL text NOT validated%N") end abort } ; ---------------------- body --------------------- attr_vals: attr_val { debug("dADL_parse") io.put_string(indent + "attr_val complete%N") end } | attr_vals attr_val { debug("dADL_parse") io.put_string(indent + "attr_val complete%N") end } | attr_vals ';' attr_val { debug("dADL_parse") io.put_string(indent + "attr_val complete%N") end } ; attr_val: attr_id SYM_EQ object_block -- could be a single or multiple attr { debug("dADL_parse") io.put_string(indent + "attr_val: POP attr node (" + attr_nodes.item.rm_attr_name+ ")%N") indent.remove_tail(1) end attr_nodes.remove } ; attr_id: V_ATTRIBUTE_IDENTIFIER { -- create first anonymous object if complex_object_nodes.is_empty then debug("dADL_parse") io.put_string(indent + "attr_id: create complex_object_node.make_anonymous%N") io.put_string(indent + "attr_id: PUSH Object node%N") indent.append("%T") end create complex_object_node.make_anonymous complex_object_nodes.extend(complex_object_node) end debug("dADL_parse") io.put_string(indent + "attr_id: create_attr_node.make_single(<<" + $1 + ">>)%N") end create attr_node.make_single($1) debug("dADL_parse") io.put_string(indent + "attr_id: complex_object_nodes.item(" + complex_object_nodes.item.node_id + ").put_attribute(<<" + attr_node.rm_attr_name + ">>)%N") end if not complex_object_nodes.item.has_attribute(attr_node.rm_attr_name) then complex_object_nodes.item.put_attribute(attr_node) else raise_error report_error("Duplicate relationship: " + attr_node.rm_attr_name) abort end debug("dADL_parse") io.put_string(indent + "attr_id: PUSH attr node%N") indent.append("%T") end attr_nodes.extend(attr_node) obj_id := Void } | V_ATTRIBUTE_IDENTIFIER error { raise_error report_error("Error in attribute value") abort } ; -- -- An object_block is any block enclosed in <> with optional leading type name -- object_block: complex_object_block | primitive_object_block ; complex_object_block: single_attr_object_block { $$ := $1 } | multiple_attr_object_block { $$ := $1 } ; -- -- --------------------------- multiple attr objects ------------------------------- -- multiple_attr_object_block: untyped_multiple_attr_object_block { $$ := $1 } | type_identifier untyped_multiple_attr_object_block { -- probably should set type name on owning attribute - it doesn't belong on each -- object, since it is essentially a constraint $$ := $2 } ; -- -- An untyped_multiple_attr_object_block is a block of the form: -- < -- [key1] = <...> -- [key2] = <...> -- [keyN] = <...> -- > -- untyped_multiple_attr_object_block: multiple_attr_object_block_head keyed_objects SYM_END_DBLOCK { if complex_object_nodes.item.is_addressable and attr_nodes.item.is_generic then -- pop the generic attr node debug("dADL_parse") io.put_string(indent + "multiple_attr_object_block: POP attr node (" + attr_nodes.item.rm_attr_name+ ")%N") indent.remove_tail(1) end attr_nodes.remove debug("dADL_parse") io.put_string(indent + "multiple_attr_object_block: POP Object node(" + complex_object_nodes.item.node_id + ")%N") indent.remove_tail(1) end $$ := complex_object_nodes.item complex_object_nodes.remove end } ; multiple_attr_object_block_head: SYM_START_DBLOCK { if obj_id /= Void then -- we are in a multi-block which is the value of a keyed object -- so create the object with the key id create complex_object_node.make_identified(obj_id) if not attr_nodes.item.has_child_with_id(complex_object_node.node_id) then debug("dADL_parse") io.put_string(indent + "multiple_attr_object_block_head; attr_nodes(<<" + attr_nodes.item.rm_attr_name + ">>).item.put_child(complex_object_node(" + complex_object_node.node_id + "))%N") end attr_nodes.item.put_child(complex_object_node) else raise_error report_error("Key must be unique; key [" + complex_object_node.node_id + "] already exists under attribute %"" + attr_nodes.item.rm_attr_name + "%"") abort end debug("dADL_parse") io.put_string(indent + "multiple_attr_object_block_head: PUSH Obj node%N") indent.append("%T") end complex_object_nodes.extend(complex_object_node) -- now create a generic attribute node to stand for the hidden attribute of the -- generic object, e.g. it might be List.items or whatever debug("dADL_parse") io.put_string(indent + "multiple_attr_object_block_head: create_attr_node.make_multiple_generic%N") end create attr_node.make_multiple_generic debug("dADL_parse") io.put_string(indent + "multiple_attr_object_block_head: complex_object_node(" + complex_object_node.node_id + ").put_attribute(" + attr_node.rm_attr_name + ")%N") end if not complex_object_node.has_attribute(attr_node.rm_attr_name) then complex_object_node.put_attribute(attr_node) else raise_error report_error("Duplicate relationship: " + attr_node.rm_attr_name) abort end debug("dADL_parse") io.put_string(indent + "multiple_attr_object_block_head: PUSH attr node%N") indent.append("%T") end attr_nodes.extend(attr_node) end } ; keyed_objects: keyed_object { debug("dADL_parse") io.put_string(indent + "one keyed object%N") end } | keyed_objects keyed_object { debug("dADL_parse") io.put_string(indent + "multiple keyed objects%N") end } ; keyed_object: object_key SYM_EQ object_block { debug("dADL_parse") io.put_string(indent + "(keyed object)%N") end } ; object_key: '[' simple_value ']' { obj_id := $2.out debug("dADL_parse") io.put_string(indent + "object_key: " + obj_id + " (setting " + attr_nodes.item.rm_attr_name + " to Multiple)%N") end if not attr_nodes.is_empty then attr_nodes.item.set_multiple else raise_error report_error("generic object not enclosed by normal object not allowed") abort end } ; -- -- --------------------------- single attr objects ------------------------------- -- single_attr_object_block: untyped_single_attr_object_block { debug("dADL_parse") io.put_string(indent + "untyped single_attr_object_block%N") end $$ := $1 } | type_identifier untyped_single_attr_object_block { debug("dADL_parse") io.put_string(indent + "typed single_attr_object_block; type = " + $1 + "%N") end $2.set_type_name($1) $$ := $2 } ; -- -- An untyped_single_attr_object_block is any block of the form: -- < -- attr1 = <...> -- attr2 = <...> -- attrN = <...> -- > -- untyped_single_attr_object_block: single_attr_object_complex_head SYM_END_DBLOCK { debug("dADL_parse") io.put_string(indent + "empty_object_complex_body: POP Object node(" + complex_object_nodes.item.node_id + ")%N") indent.remove_tail(1) end $$ := complex_object_nodes.item complex_object_nodes.remove } | single_attr_object_complex_head attr_vals SYM_END_DBLOCK { debug("dADL_parse") io.put_string(indent + "single_attr_object_complex_body: POP Object node(" + complex_object_nodes.item.node_id + ")%N") indent.remove_tail(1) end $$ := complex_object_nodes.item complex_object_nodes.remove } ; single_attr_object_complex_head: SYM_START_DBLOCK { -- if parent attr is not multiple, create an anon object; else an object identified by a key if attr_nodes.is_empty or else not attr_nodes.item.is_multiple then debug("dADL_parse") io.put_string(indent + "single_attr_object_complex_head: create complex_object_node.make_anonymous%N") end create complex_object_node.make_anonymous else debug("dADL_parse") io.put_string(indent + "single_attr_object_complex_head: create complex_object_node.make (" + obj_id + ")%N") end create complex_object_node.make_identified(obj_id) obj_id := Void end -- now put the new object under its attribute, if one exists if not attr_nodes.is_empty then if not attr_nodes.item.has_child_with_id(complex_object_node.node_id) then debug("dADL_parse") io.put_string(indent + "single_attr_object_complex_head: attr_nodes(<<" + attr_nodes.item.rm_attr_name + ">>).item.put_child(complex_object_node(" + complex_object_node.node_id + "))%N") end attr_nodes.item.put_child(complex_object_node) else raise_error report_error("Key must be unique; key [" + complex_object_node.node_id + "] already exists under attribute %"" + attr_nodes.item.rm_attr_name + "%"") abort end end -- finally, put the new object on the object stack debug("dADL_parse") io.put_string(indent + "single_attr_object_complex_head: PUSH Obj node%N") indent.append("%T") end complex_object_nodes.extend(complex_object_node) } ; -- -- ------------------------- primitive objects --------------------- -- primitive_object_block: untyped_primitive_object_block { debug("dADL_parse") io.put_string(indent + "untyped primitive_object_block%N") end $$ := $1 } | type_identifier untyped_primitive_object_block { debug("dADL_parse") io.put_string(indent + "typed primitive_object_block; type = " + $1 + "%N") end $2.set_type_name($1) $$ := $2 } ; untyped_primitive_object_block: SYM_START_DBLOCK primitive_object_value SYM_END_DBLOCK { debug("dADL_parse") io.put_string(indent + "untyped_primitive_object_block; attr_nodes(<<" + attr_nodes.item.rm_attr_name + ">>).item.put_child(<<" + leaf_object_node.as_string + ">>)%N") end if not attr_nodes.item.has_child_with_id(leaf_object_node.node_id) then attr_nodes.item.put_child(leaf_object_node) $$ := leaf_object_node else raise_error report_error("Key must be unique; key [" + leaf_object_node.node_id + "] already exists under attribute %"" + attr_nodes.item.rm_attr_name + "%"") abort end } ; primitive_object_value: simple_value { if obj_id /= Void then create {DT_PRIMITIVE_OBJECT} leaf_object_node.make_identified($1, obj_id) obj_id := Void else create {DT_PRIMITIVE_OBJECT} leaf_object_node.make_anonymous($1) end } | simple_list_value { if obj_id /= Void then create {DT_PRIMITIVE_OBJECT_LIST} leaf_object_node.make_identified($1, obj_id) obj_id := Void else create {DT_PRIMITIVE_OBJECT_LIST} leaf_object_node.make_anonymous($1) end } | simple_interval_value { if obj_id /= Void then create {DT_PRIMITIVE_OBJECT_INTERVAL} leaf_object_node.make_identified($1, obj_id) obj_id := Void else create {DT_PRIMITIVE_OBJECT_INTERVAL} leaf_object_node.make_anonymous($1) end } | term_code { if obj_id /= Void then create {DT_PRIMITIVE_OBJECT} leaf_object_node.make_identified($1, obj_id) obj_id := Void else create {DT_PRIMITIVE_OBJECT} leaf_object_node.make_anonymous($1) end } | term_code_list_value { if obj_id /= Void then create {DT_PRIMITIVE_OBJECT_LIST} leaf_object_node.make_identified($1, obj_id) obj_id := Void else create {DT_PRIMITIVE_OBJECT_LIST} leaf_object_node.make_anonymous($1) end } ; simple_value: string_value { $$ := $1 } | integer_value { $$ := $1 } | real_value { $$ := $1 } | boolean_value { $$ := $1 } | character_value { $$ := $1 } | date_value { $$ := $1 } | time_value { $$ := $1 } | date_time_value { $$ := $1 } | duration_value { $$ := $1 } | uri_value { $$ := $1 } ; simple_list_value: string_list_value { $$ := $1 } | integer_list_value { $$ := $1 } | real_list_value { $$ := $1 } | boolean_list_value { $$ := $1 } | character_list_value { $$ := $1 } | date_list_value { $$ := $1 } | time_list_value { $$ := $1 } | date_time_list_value { $$ := $1 } | duration_list_value { $$ := $1 } ; simple_interval_value: integer_interval_value { $$ := $1 } | real_interval_value { $$ := $1 } | date_interval_value { $$ := $1 } | time_interval_value { $$ := $1 } | date_time_interval_value { $$ := $1 } | duration_interval_value { $$ := $1 } ; ---------------------- BASIC DATA VALUES ----------------------- type_identifier: '(' V_TYPE_IDENTIFIER ')' { $$ := $2 } | '(' V_GENERIC_TYPE_IDENTIFIER ')' { $$ := $2 } | V_TYPE_IDENTIFIER { $$ := $1 } | V_GENERIC_TYPE_IDENTIFIER { $$ := $1 } ; string_value: V_STRING { $$ := $1 } ; string_list_value: V_STRING ',' V_STRING { create $$.make(0) $$.extend($1) $$.extend($3) } | string_list_value ',' V_STRING { $1.extend($3) $$ := $1 } | V_STRING ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; integer_value: V_INTEGER { $$ := $1 } | '+' V_INTEGER { $$ := $2 } | '-' V_INTEGER { $$ := - $2 } ; integer_list_value: integer_value ',' integer_value { create $$.make(0) $$.extend($1) $$.extend($3) } | integer_list_value ',' integer_value { $1.extend($3) $$ := $1 } | integer_value ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; integer_interval_value: SYM_INTERVAL_DELIM integer_value SYM_ELLIPSIS integer_value SYM_INTERVAL_DELIM { if $2 <= $4 then create integer_interval.make_bounded($2, $4, True, True) $$ := integer_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $4.out) abort end } | SYM_INTERVAL_DELIM SYM_GT integer_value SYM_ELLIPSIS integer_value SYM_INTERVAL_DELIM { if $3 <= $5 then create integer_interval.make_bounded($3, $5, False, True) $$ := integer_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM integer_value SYM_ELLIPSIS SYM_LT integer_value SYM_INTERVAL_DELIM { if $2 <= $5 then create integer_interval.make_bounded($2, $5, True, False) $$ := integer_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM SYM_GT integer_value SYM_ELLIPSIS SYM_LT integer_value SYM_INTERVAL_DELIM { if $3 <= $6 then create integer_interval.make_bounded($3, $6, False, False) $$ := integer_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $6.out) abort end } | SYM_INTERVAL_DELIM SYM_LT integer_value SYM_INTERVAL_DELIM { create integer_interval.make_lower_unbounded($3, False) $$ := integer_interval } | SYM_INTERVAL_DELIM SYM_LE integer_value SYM_INTERVAL_DELIM { create integer_interval.make_lower_unbounded($3, True) $$ := integer_interval } | SYM_INTERVAL_DELIM SYM_GT integer_value SYM_INTERVAL_DELIM { create integer_interval.make_upper_unbounded($3, False) $$ := integer_interval } | SYM_INTERVAL_DELIM SYM_GE integer_value SYM_INTERVAL_DELIM { create integer_interval.make_upper_unbounded($3, True) $$ := integer_interval } | SYM_INTERVAL_DELIM integer_value SYM_INTERVAL_DELIM { create integer_interval.make_point($2) $$ := integer_interval } ; real_value: V_REAL { $$ := $1 } | '+' V_REAL { $$ := $2 } | '-' V_REAL { $$ := - $2 } ; real_list_value: real_value ',' real_value { create $$.make(0) $$.extend($1) $$.extend($3) } | real_list_value ',' real_value { $1.extend($3) $$ := $1 } | real_value ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; real_interval_value: SYM_INTERVAL_DELIM real_value SYM_ELLIPSIS real_value SYM_INTERVAL_DELIM { if $2 <= $4 then create real_interval.make_bounded($2, $4, True, True) $$ := real_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $4.out) abort end } | SYM_INTERVAL_DELIM SYM_GT real_value SYM_ELLIPSIS real_value SYM_INTERVAL_DELIM { if $3 <= $5 then create real_interval.make_bounded($3, $5, False, True) $$ := real_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM real_value SYM_ELLIPSIS SYM_LT real_value SYM_INTERVAL_DELIM { if $2 <= $5 then create real_interval.make_bounded($2, $5, True, False) $$ := real_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM SYM_GT real_value SYM_ELLIPSIS SYM_LT real_value SYM_INTERVAL_DELIM { if $3 <= $6 then create real_interval.make_bounded($3, $6, False, False) $$ := real_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $6.out) abort end } | SYM_INTERVAL_DELIM SYM_LT real_value SYM_INTERVAL_DELIM { create real_interval.make_lower_unbounded($3, False) $$ := real_interval } | SYM_INTERVAL_DELIM SYM_LE real_value SYM_INTERVAL_DELIM { create real_interval.make_lower_unbounded($3, True) $$ := real_interval } | SYM_INTERVAL_DELIM SYM_GT real_value SYM_INTERVAL_DELIM { create real_interval.make_upper_unbounded($3, False) $$ := real_interval } | SYM_INTERVAL_DELIM SYM_GE real_value SYM_INTERVAL_DELIM { create real_interval.make_upper_unbounded($3, True) $$ := real_interval } | SYM_INTERVAL_DELIM real_value SYM_INTERVAL_DELIM { create real_interval.make_point($2) $$ := real_interval } ; boolean_value: SYM_TRUE { $$ := True } | SYM_FALSE { $$ := False } ; boolean_list_value: boolean_value ',' boolean_value { create $$.make(0) $$.extend($1) $$.extend($3) } | boolean_list_value ',' boolean_value { $1.extend($3) $$ := $1 } | boolean_value ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; character_value: V_CHARACTER { $$ := $1 } ; character_list_value: character_value ',' character_value { create $$.make(0) $$.extend($1) $$.extend($3) } | character_list_value ',' character_value { $1.extend($3) $$ := $1 } | character_value ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; date_value: V_ISO8601_EXTENDED_DATE -- in ISO8601 form yyyy-MM-dd { if valid_iso8601_date($1) then create $$.make_from_string($1) else raise_error report_error("invalid ISO8601 date: " + $1) abort end } ; date_list_value: date_value ',' date_value { create $$.make(0) $$.extend($1) $$.extend($3) } | date_list_value ',' date_value { $1.extend($3) $$ := $1 } | date_value ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; date_interval_value: SYM_INTERVAL_DELIM date_value SYM_ELLIPSIS date_value SYM_INTERVAL_DELIM { if $2 <= $4 then create date_interval.make_bounded($2, $4, True, True) $$ := date_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $4.out) abort end } | SYM_INTERVAL_DELIM SYM_GT date_value SYM_ELLIPSIS date_value SYM_INTERVAL_DELIM { if $3 <= $5 then create date_interval.make_bounded($3, $5, False, True) $$ := date_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM date_value SYM_ELLIPSIS SYM_LT date_value SYM_INTERVAL_DELIM { if $2 <= $5 then create date_interval.make_bounded($2, $5, True, False) $$ := date_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM SYM_GT date_value SYM_ELLIPSIS SYM_LT date_value SYM_INTERVAL_DELIM { if $3 <= $6 then create date_interval.make_bounded($3, $6, False, False) $$ := date_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $6.out) abort end } | SYM_INTERVAL_DELIM SYM_LT date_value SYM_INTERVAL_DELIM { create date_interval.make_lower_unbounded($3, False) $$ := date_interval } | SYM_INTERVAL_DELIM SYM_LE date_value SYM_INTERVAL_DELIM { create date_interval.make_lower_unbounded($3, True) $$ := date_interval } | SYM_INTERVAL_DELIM SYM_GT date_value SYM_INTERVAL_DELIM { create date_interval.make_upper_unbounded($3, False) $$ := date_interval } | SYM_INTERVAL_DELIM SYM_GE date_value SYM_INTERVAL_DELIM { create date_interval.make_upper_unbounded($3, True) $$ := date_interval } | SYM_INTERVAL_DELIM date_value SYM_INTERVAL_DELIM { create date_interval.make_point($2) $$ := date_interval } ; time_value: V_ISO8601_EXTENDED_TIME { if valid_iso8601_time($1) then create $$.make_from_string($1) else raise_error report_error("invalid ISO8601 time: " + $1) abort end } ; time_list_value: time_value ',' time_value { create $$.make(0) $$.extend($1) $$.extend($3) } | time_list_value ',' time_value { $1.extend($3) $$ := $1 } | time_value ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; time_interval_value: SYM_INTERVAL_DELIM time_value SYM_ELLIPSIS time_value SYM_INTERVAL_DELIM { if $2 <= $4 then create time_interval.make_bounded($2, $4, True, True) $$ := time_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $4.out) abort end } | SYM_INTERVAL_DELIM SYM_GT time_value SYM_ELLIPSIS time_value SYM_INTERVAL_DELIM { if $3 <= $5 then create time_interval.make_bounded($3, $5, False, True) $$ := time_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM time_value SYM_ELLIPSIS SYM_LT time_value SYM_INTERVAL_DELIM { if $2 <= $5 then create time_interval.make_bounded($2, $5, True, False) $$ := time_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM SYM_GT time_value SYM_ELLIPSIS SYM_LT time_value SYM_INTERVAL_DELIM { if $3 <= $6 then create time_interval.make_bounded($3, $6, False, False) $$ := time_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $6.out) abort end } | SYM_INTERVAL_DELIM SYM_LT time_value SYM_INTERVAL_DELIM { create time_interval.make_lower_unbounded($3, False) $$ := time_interval } | SYM_INTERVAL_DELIM SYM_LE time_value SYM_INTERVAL_DELIM { create time_interval.make_lower_unbounded($3, True) $$ := time_interval } | SYM_INTERVAL_DELIM SYM_GT time_value SYM_INTERVAL_DELIM { create time_interval.make_upper_unbounded($3, False) $$ := time_interval } | SYM_INTERVAL_DELIM SYM_GE time_value SYM_INTERVAL_DELIM { create time_interval.make_upper_unbounded($3, True) $$ := time_interval } | SYM_INTERVAL_DELIM time_value SYM_INTERVAL_DELIM { create time_interval.make_point($2) $$ := time_interval } ; date_time_value: V_ISO8601_EXTENDED_DATE_TIME { if valid_iso8601_date_time($1) then create $$.make_from_string($1) else raise_error report_error("invalid ISO8601 date/time: " + $1) abort end } ; date_time_list_value: date_time_value ',' date_time_value { create $$.make(0) $$.extend($1) $$.extend($3) } | date_time_list_value ',' date_time_value { $1.extend($3) $$ := $1 } | date_time_value ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; date_time_interval_value: SYM_INTERVAL_DELIM date_time_value SYM_ELLIPSIS date_time_value SYM_INTERVAL_DELIM { if $2 <= $4 then create date_time_interval.make_bounded($2, $4, True, True) $$ := date_time_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $4.out) abort end } | SYM_INTERVAL_DELIM SYM_GT date_time_value SYM_ELLIPSIS date_time_value SYM_INTERVAL_DELIM { if $3 <= $5 then create date_time_interval.make_bounded($3, $5, False, True) $$ := date_time_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM date_time_value SYM_ELLIPSIS SYM_LT date_time_value SYM_INTERVAL_DELIM { if $2 <= $5 then create date_time_interval.make_bounded($2, $5, True, False) $$ := date_time_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM SYM_GT date_time_value SYM_ELLIPSIS SYM_LT date_time_value SYM_INTERVAL_DELIM { if $3 <= $6 then create date_time_interval.make_bounded($3, $6, False, False) $$ := date_time_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $6.out) abort end } | SYM_INTERVAL_DELIM SYM_LT date_time_value SYM_INTERVAL_DELIM { create date_time_interval.make_lower_unbounded($3, False) $$ := date_time_interval } | SYM_INTERVAL_DELIM SYM_LE date_time_value SYM_INTERVAL_DELIM { create date_time_interval.make_lower_unbounded($3, True) $$ := date_time_interval } | SYM_INTERVAL_DELIM SYM_GT date_time_value SYM_INTERVAL_DELIM { create date_time_interval.make_upper_unbounded($3, False) $$ := date_time_interval } | SYM_INTERVAL_DELIM SYM_GE date_time_value SYM_INTERVAL_DELIM { create date_time_interval.make_upper_unbounded($3, True) $$ := date_time_interval } | SYM_INTERVAL_DELIM date_time_value SYM_INTERVAL_DELIM { create date_time_interval.make_point($2) $$ := date_time_interval } ; duration_value: V_ISO8601_DURATION { if valid_iso8601_duration($1) then create $$.make_from_string($1) else raise_error report_error("invalid ISO8601 duration: " + $1) abort end } ; duration_list_value: duration_value ',' duration_value { create $$.make(0) $$.extend($1) $$.extend($3) } | duration_list_value ',' duration_value { $1.extend($3) $$ := $1 } | duration_value ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; duration_interval_value: SYM_INTERVAL_DELIM duration_value SYM_ELLIPSIS duration_value SYM_INTERVAL_DELIM { if $2 <= $4 then create duration_interval.make_bounded($2, $4, True, True) $$ := duration_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $4.out) abort end } | SYM_INTERVAL_DELIM SYM_GT duration_value SYM_ELLIPSIS duration_value SYM_INTERVAL_DELIM { if $3 <= $5 then create duration_interval.make_bounded($3, $5, False, True) $$ := duration_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM duration_value SYM_ELLIPSIS SYM_LT duration_value SYM_INTERVAL_DELIM { if $2 <= $5 then create duration_interval.make_bounded($2, $5, True, False) $$ := duration_interval else raise_error report_error("Invalid interval: " + $2.out + " must be <= " + $5.out) abort end } | SYM_INTERVAL_DELIM SYM_GT duration_value SYM_ELLIPSIS SYM_LT duration_value SYM_INTERVAL_DELIM { if $3 <= $6 then create duration_interval.make_bounded($3, $6, False, False) $$ := duration_interval else raise_error report_error("Invalid interval: " + $3.out + " must be <= " + $6.out) abort end } | SYM_INTERVAL_DELIM SYM_LT duration_value SYM_INTERVAL_DELIM { create duration_interval.make_lower_unbounded($3, False) $$ := duration_interval } | SYM_INTERVAL_DELIM SYM_LE duration_value SYM_INTERVAL_DELIM { create duration_interval.make_lower_unbounded($3, True) $$ := duration_interval } | SYM_INTERVAL_DELIM SYM_GT duration_value SYM_INTERVAL_DELIM { create duration_interval.make_upper_unbounded($3, False) $$ := duration_interval } | SYM_INTERVAL_DELIM SYM_GE duration_value SYM_INTERVAL_DELIM { create duration_interval.make_upper_unbounded($3, True) $$ := duration_interval } | SYM_INTERVAL_DELIM duration_value SYM_INTERVAL_DELIM { create duration_interval.make_point($2) $$ := duration_interval } ; term_code: V_QUALIFIED_TERM_CODE_REF { create term.make_from_string($1) $$ := term } | ERR_V_QUALIFIED_TERM_CODE_REF { raise_error report_error("Invalid term code reference: %"" + $1 + "%"; spaces not allowed in code string") abort } ; term_code_list_value: term_code ',' term_code { create $$.make(0) $$.extend($1) $$.extend($3) } | term_code_list_value ',' term_code { $1.extend($3) $$ := $1 } | term_code ',' SYM_LIST_CONTINUE { create $$.make(0) $$.extend($1) } ; uri_value: V_URI { create a_uri.make_from_string($1) $$ := a_uri } ; %% feature -- Initialization make is -- Create a new parser. do make_scanner make_parser_skeleton end execute(in_text:STRING; a_source_start_line: INTEGER) is do reset_scanner accept -- ensure no syntax errors lying around from previous invocation source_start_line := a_source_start_line create indent.make(0) create error_text.make(0) create error_message.make(0) create complex_object_nodes.make(0) create attr_nodes.make(0) create time_vc create date_vc set_input_buffer (new_string_buffer (in_text)) parse end feature {YY_PARSER_ACTION} -- Basic Operations report_error (a_message: STRING) is -- Print error message. local f_buffer: YY_FILE_BUFFER do error_message.append (a_message + " [last dADL token = " + token_name(last_token) + "]") f_buffer ?= input_buffer if f_buffer /= Void then error_text.append (f_buffer.file.name + ", line ") else error_text.append ("line ") end error_text.append ((in_lineno + source_start_line).out + ": " + error_message + "%N") end feature -- Access error_text: STRING -- complete error text including line number location error_message: STRING -- message part of error text source_start_line: INTEGER -- offset of source in other document, else 0 output: DT_COMPLEX_OBJECT_NODE -- parsed structure feature {NONE} -- Parse Tree complex_object_nodes: ARRAYED_STACK[DT_COMPLEX_OBJECT_NODE] complex_object_node: DT_COMPLEX_OBJECT_NODE leaf_object_node: DT_OBJECT_LEAF last_object_node: DT_OBJECT_ITEM attr_nodes: ARRAYED_STACK[DT_ATTRIBUTE_NODE] attr_node: DT_ATTRIBUTE_NODE obj_id: STRING -- qualifier of last rel name; use for next object creation rm_attr_name: STRING node_type: STRING node_id: STRING time_vc: TIME_VALIDITY_CHECKER date_vc: DATE_VALIDITY_CHECKER feature {NONE} -- Implementation term: CODE_PHRASE a_uri: URI integer_interval: INTERVAL [INTEGER_REF] real_interval: INTERVAL [REAL_REF] date_interval: INTERVAL [ISO8601_DATE] time_interval: INTERVAL [ISO8601_TIME] date_time_interval: INTERVAL [ISO8601_DATE_TIME] duration_interval: INTERVAL [ISO8601_DURATION] indent: STRING str: STRING end