YAML: handle recursive data and anchors' identity
parent
8a7b8eb169
commit
559f140a93
|
@ -1,6 +1,7 @@
|
|||
! Copyright (C) 2014 Jon Harper.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: assocs linked-assocs literals tools.test yaml ;
|
||||
USING: assocs kernel linked-assocs literals locals sequences
|
||||
tools.test yaml yaml.private grouping ;
|
||||
IN: yaml.tests
|
||||
|
||||
! TODO real conformance tests here
|
||||
|
@ -94,10 +95,64 @@ CONSTANT: test-anchors-obj {
|
|||
|
||||
${ test-anchors-obj } [ $ test-anchors yaml> ] unit-test
|
||||
${ test-anchors-obj } [ $ test-anchors-obj >yaml yaml> ] unit-test
|
||||
! and test indentity
|
||||
{ t } [ $ test-anchors yaml> 2 group [ all-eq? ] all? ] unit-test
|
||||
{ t } [ $ test-anchors yaml> >yaml yaml> 2 group [ all-eq? ] all? ] unit-test
|
||||
|
||||
! Anchors and fancy types
|
||||
CONSTANT: fancy-anchors """- &1 [ "foo" ]
|
||||
- &2 !!set
|
||||
? *1
|
||||
- *2
|
||||
"""
|
||||
CONSTANT: fancy-anchors-obj {
|
||||
{ "foo" } HS{ { "foo" } } HS{ { "foo" } }
|
||||
}
|
||||
${ fancy-anchors-obj } [ $ fancy-anchors yaml> ] unit-test
|
||||
${ fancy-anchors-obj } [ $ fancy-anchors-obj >yaml yaml> ] unit-test
|
||||
|
||||
! Missing anchors
|
||||
[ "*foo" yaml> ] [ "No previous anchor" = ] must-fail-with
|
||||
|
||||
! Simple Recursive output
|
||||
: simple-recursive-list ( -- obj )
|
||||
{ f } clone [ 0 over set-nth ] keep ;
|
||||
CONSTANT: simple-recursive-list-anchored T{ yaml-anchor f "0" {
|
||||
T{ yaml-alias f "0" }
|
||||
} }
|
||||
CONSTANT: simple-recursive-list-yaml """&0
|
||||
- *0"""
|
||||
|
||||
${ simple-recursive-list-anchored } [ simple-recursive-list replace-identities ] unit-test
|
||||
${ simple-recursive-list-anchored } [ $ simple-recursive-list-yaml yaml> replace-identities ] unit-test
|
||||
${ simple-recursive-list-anchored } [ simple-recursive-list >yaml yaml> replace-identities ] unit-test
|
||||
|
||||
! many recursive outputs
|
||||
: many-recursive-objects ( -- obj )
|
||||
4 [ simple-recursive-list ] replicate ;
|
||||
CONSTANT: many-recursive-objects-anchored {
|
||||
T{ yaml-anchor f "0" { T{ yaml-alias f "0" } } }
|
||||
T{ yaml-anchor f "1" { T{ yaml-alias f "1" } } }
|
||||
T{ yaml-anchor f "2" { T{ yaml-alias f "2" } } }
|
||||
T{ yaml-anchor f "3" { T{ yaml-alias f "3" } } }
|
||||
}
|
||||
|
||||
${ many-recursive-objects-anchored } [ many-recursive-objects replace-identities ] unit-test
|
||||
|
||||
! Advanced recursive outputs
|
||||
:: transitive-recursive-objects ( -- obj )
|
||||
{ f } :> list
|
||||
HS{ list } :> set
|
||||
H{ { set list } } :> hash
|
||||
hash 0 list set-nth
|
||||
list ;
|
||||
CONSTANT: transitive-recursive-objects-anchored T{ yaml-anchor f "0" {
|
||||
H{ { HS{ T{ yaml-alias f "0" } } T{ yaml-alias f "0" } } }
|
||||
} }
|
||||
|
||||
${ transitive-recursive-objects-anchored } [ transitive-recursive-objects replace-identities ] unit-test
|
||||
|
||||
|
||||
! Lifted from pyyaml
|
||||
! http://pyyaml.org/browser/pyyaml/trunk/tests/data
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
! Copyright (C) 2013 Jon Harper.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: accessors alien.data arrays assocs byte-arrays
|
||||
classes.struct combinators destructors hashtables
|
||||
io.encodings.string io.encodings.utf8 kernel libc linked-assocs
|
||||
locals make namespaces sequences sets strings yaml.conversion
|
||||
yaml.ffi ;
|
||||
classes.struct combinators combinators.extras
|
||||
combinators.short-circuit destructors fry hashtables
|
||||
hashtables.identity io.encodings.string io.encodings.utf8 kernel
|
||||
libc linked-assocs locals make math math.parser namespaces
|
||||
sequences sets strings yaml.conversion yaml.ffi ;
|
||||
FROM: sets => set ;
|
||||
IN: yaml
|
||||
|
||||
|
@ -12,13 +13,20 @@ IN: yaml
|
|||
|
||||
: yaml-assert-ok ( ? -- ) [ "yaml error" throw ] unless ;
|
||||
|
||||
TUPLE: yaml-alias anchor ;
|
||||
C: <yaml-alias> yaml-alias
|
||||
SYMBOL: anchors
|
||||
: ?register-anchor ( obj event -- obj )
|
||||
dupd anchor>> [ anchors get set-at ] [ drop ] if* ;
|
||||
: deref-anchor ( event -- obj )
|
||||
data>> alias>> anchor>> anchors get at*
|
||||
: assert-anchor-exists ( anchor -- )
|
||||
anchors get at* nip
|
||||
[ "No previous anchor" throw ] unless ;
|
||||
|
||||
: deref-anchor ( event -- obj )
|
||||
data>> alias>> anchor>>
|
||||
[ assert-anchor-exists ]
|
||||
[ <yaml-alias> ] bi ;
|
||||
|
||||
: event>scalar ( event -- obj )
|
||||
data>> scalar>>
|
||||
[ construct-scalar ]
|
||||
|
@ -59,11 +67,11 @@ TUPLE: factor_yaml_event_t type data start_mark end_mark ;
|
|||
DEFER: parse-sequence
|
||||
DEFER: parse-mapping
|
||||
: (parse-sequence) ( parser event prev-event -- obj )
|
||||
data>> sequence_start>>
|
||||
[ [ parse-sequence ] [ construct-sequence ] bi* ] [ 2nip ?register-anchor ] 3bi ;
|
||||
data>> sequence_start>> [ [ 2drop f ] dip ?register-anchor drop ]
|
||||
[ [ parse-sequence ] [ construct-sequence ] bi* ] [ 2nip ?register-anchor ] 3tri ;
|
||||
: (parse-mapping) ( parser event prev-event -- obj )
|
||||
data>> mapping_start>>
|
||||
[ [ parse-mapping ] [ construct-mapping ] bi* ] [ 2nip ?register-anchor ] 3bi ;
|
||||
data>> mapping_start>> [ [ 2drop f ] dip ?register-anchor drop ]
|
||||
[ [ parse-mapping ] [ construct-mapping ] bi* ] [ 2nip ?register-anchor ] 3tri ;
|
||||
: next-complex-value ( parser event prev-event -- obj )
|
||||
dup type>> {
|
||||
{ YAML_SEQUENCE_START_EVENT [ (parse-sequence) ] }
|
||||
|
@ -118,9 +126,25 @@ DEFER: parse-mapping
|
|||
[ "wrong event" throw ] unless
|
||||
] with-destructors ;
|
||||
|
||||
GENERIC: (deref-aliases) ( anchors obj -- obj' )
|
||||
M: object (deref-aliases) nip ;
|
||||
M: byte-array (deref-aliases) nip ;
|
||||
M: string (deref-aliases) nip ;
|
||||
M: yaml-alias (deref-aliases) anchor>> swap at ;
|
||||
|
||||
M: sequence (deref-aliases)
|
||||
[ (deref-aliases) ] with map! ;
|
||||
M: set (deref-aliases)
|
||||
[ members (deref-aliases) ] [ clear-set ] [ swap union! ] tri ;
|
||||
: assoc-map! ( assoc quot -- )
|
||||
[ assoc-map ] [ drop clear-assoc ] [ drop swap assoc-union! ] 2tri ; inline
|
||||
M: assoc (deref-aliases)
|
||||
swap '[ [ _ swap (deref-aliases) ] bi@ ] assoc-map! ;
|
||||
|
||||
:: parse-yaml-doc ( parser event -- obj )
|
||||
H{ } clone anchors [
|
||||
parser event next-value
|
||||
anchors get swap (deref-aliases)
|
||||
] with-variable ;
|
||||
|
||||
:: ?parse-yaml-doc ( parser event -- obj/f ? )
|
||||
|
@ -167,6 +191,59 @@ PRIVATE>
|
|||
|
||||
<PRIVATE
|
||||
|
||||
TUPLE: yaml-anchors objects new-objects next-anchor ;
|
||||
: <yaml-anchors> ( -- yaml-anchors )
|
||||
IH{ } clone IH{ } clone 0 yaml-anchors boa ;
|
||||
GENERIC: (replace-aliases) ( yaml-anchors obj -- obj' )
|
||||
: incr-anchor ( yaml-anchors -- current-anchor )
|
||||
[ next-anchor>> ] [
|
||||
[ [ number>string ] [ 1 + ] bi ]
|
||||
[ next-anchor<< ] bi*
|
||||
] bi ;
|
||||
:: ?replace-aliases ( yaml-anchors obj -- obj' )
|
||||
yaml-anchors objects>> :> objects
|
||||
obj objects at* [
|
||||
[ yaml-anchors incr-anchor dup obj objects set-at ] unless*
|
||||
<yaml-alias>
|
||||
] [
|
||||
drop f obj objects set-at
|
||||
yaml-anchors obj (replace-aliases) :> obj'
|
||||
obj obj' yaml-anchors new-objects>> set-at
|
||||
obj'
|
||||
] if ;
|
||||
|
||||
M: object (replace-aliases) nip ;
|
||||
M: byte-array (replace-aliases) nip ;
|
||||
M: string (replace-aliases) nip ;
|
||||
|
||||
M: sequence (replace-aliases)
|
||||
[ ?replace-aliases ] with map ;
|
||||
M: set (replace-aliases) [ members (replace-aliases) ] keep set-like ;
|
||||
M: assoc (replace-aliases)
|
||||
swap '[ [ _ swap ?replace-aliases ] bi@ ] assoc-map ;
|
||||
|
||||
TUPLE: yaml-anchor anchor obj ;
|
||||
C: <yaml-anchor> yaml-anchor
|
||||
|
||||
GENERIC: (replace-anchors) ( yaml-anchors obj -- obj' )
|
||||
: (get-anchor) ( yaml-anchors obj -- anchor/f ) swap objects>> at ;
|
||||
: get-anchor ( yaml-anchors obj -- anchor/f )
|
||||
{ [ (get-anchor) ] [ over new-objects>> at (get-anchor) ] } 2|| ;
|
||||
: ?replace-anchors ( yaml-anchors obj -- obj' )
|
||||
[ (replace-anchors) ] [ get-anchor ] 2bi [ swap <yaml-anchor> ] when* ;
|
||||
M: object (replace-anchors) nip ;
|
||||
M: byte-array (replace-anchors) nip ;
|
||||
M: string (replace-anchors) nip ;
|
||||
|
||||
M: sequence (replace-anchors)
|
||||
[ ?replace-anchors ] with map ;
|
||||
M: set (replace-anchors) [ members ?replace-anchors ] keep set-like ;
|
||||
M: assoc (replace-anchors)
|
||||
swap '[ [ _ swap ?replace-anchors ] bi@ ] assoc-map ;
|
||||
|
||||
: replace-identities ( obj -- obj' )
|
||||
[ <yaml-anchors> ] dip dupd ?replace-aliases ?replace-anchors ;
|
||||
|
||||
! TODO We can also pass some data when registering the write handler,
|
||||
! use this to have several buffers if it can be interrupted.
|
||||
! For now, only do operations on strings that are in memory
|
||||
|
@ -178,19 +255,26 @@ SYMBOL: yaml-write-buffer
|
|||
push-all drop 1
|
||||
] yaml_write_handler_t ;
|
||||
|
||||
GENERIC: emit-value ( emitter event obj -- )
|
||||
GENERIC: emit-value ( emitter event anchor obj -- )
|
||||
: emit-object ( emitter event obj -- ) [ f ] dip emit-value ;
|
||||
|
||||
:: emit-scalar ( emitter event obj -- )
|
||||
event f
|
||||
:: emit-scalar ( emitter event anchor obj -- )
|
||||
event anchor
|
||||
obj [ yaml-tag ] [ represent-scalar ] bi
|
||||
-1 f f YAML_ANY_SCALAR_STYLE
|
||||
yaml_scalar_event_initialize yaml-assert-ok
|
||||
emitter event yaml_emitter_emit yaml-assert-ok ;
|
||||
|
||||
M: object emit-value ( emitter event obj -- ) emit-scalar ;
|
||||
M: object emit-value ( emitter event anchor obj -- ) emit-scalar ;
|
||||
|
||||
:: emit-sequence-start ( emitter event tag -- )
|
||||
event f tag f YAML_ANY_SEQUENCE_STYLE
|
||||
M: yaml-anchor emit-value ( emitter event unused obj -- )
|
||||
nip [ anchor>> ] [ obj>> ] bi emit-value ;
|
||||
M:: yaml-alias emit-value ( emitter event unused obj -- )
|
||||
event obj anchor>> yaml_alias_event_initialize yaml-assert-ok
|
||||
emitter event yaml_emitter_emit yaml-assert-ok ;
|
||||
|
||||
:: emit-sequence-start ( emitter event anchor tag -- )
|
||||
event anchor tag f YAML_ANY_SEQUENCE_STYLE
|
||||
yaml_sequence_start_event_initialize yaml-assert-ok
|
||||
emitter event yaml_emitter_emit yaml-assert-ok ;
|
||||
|
||||
|
@ -198,29 +282,29 @@ M: object emit-value ( emitter event obj -- ) emit-scalar ;
|
|||
dup yaml_sequence_end_event_initialize yaml-assert-ok
|
||||
yaml_emitter_emit yaml-assert-ok ;
|
||||
|
||||
: emit-sequence ( emitter event seq -- )
|
||||
[ emit-value ] with with each ;
|
||||
: emit-assoc ( emitter event assoc -- )
|
||||
>alist concat emit-sequence ;
|
||||
: emit-linked-assoc ( emitter event linked-assoc -- )
|
||||
>alist [ first2 swap associate ] map emit-sequence ;
|
||||
: emit-set ( emitter event set -- )
|
||||
[ members ] [ cardinality f <array> ] bi zip concat emit-sequence ;
|
||||
: emit-sequence-body ( emitter event seq -- )
|
||||
[ emit-object ] with with each ;
|
||||
: emit-assoc-body ( emitter event assoc -- )
|
||||
>alist concat emit-sequence-body ;
|
||||
: emit-linked-assoc-body ( emitter event linked-assoc -- )
|
||||
>alist [ first2 swap associate ] map emit-sequence-body ;
|
||||
: emit-set-body ( emitter event set -- )
|
||||
[ members ] [ cardinality f <array> ] bi zip concat emit-sequence-body ;
|
||||
|
||||
M: f emit-value ( emitter event seq -- ) emit-scalar ;
|
||||
M: string emit-value ( emitter event seq -- ) emit-scalar ;
|
||||
M: byte-array emit-value ( emitter event seq -- ) emit-scalar ;
|
||||
M: sequence emit-value ( emitter event seq -- )
|
||||
M: f emit-value ( emitter event anchor f -- ) emit-scalar ;
|
||||
M: string emit-value ( emitter event anchor string -- ) emit-scalar ;
|
||||
M: byte-array emit-value ( emitter event anchor byte-array -- ) emit-scalar ;
|
||||
M: sequence emit-value ( emitter event anchor seq -- )
|
||||
[ drop YAML_SEQ_TAG emit-sequence-start ]
|
||||
[ emit-sequence ]
|
||||
[ drop emit-sequence-end ] 3tri ;
|
||||
M: linked-assoc emit-value ( emitter event assoc -- )
|
||||
[ nip emit-sequence-body ]
|
||||
[ 2drop emit-sequence-end ] 4tri ;
|
||||
M: linked-assoc emit-value ( emitter event anchor assoc -- )
|
||||
[ drop YAML_OMAP_TAG emit-sequence-start ]
|
||||
[ emit-linked-assoc ]
|
||||
[ drop emit-sequence-end ] 3tri ;
|
||||
[ nip emit-linked-assoc-body ]
|
||||
[ 2drop emit-sequence-end ] 4tri ;
|
||||
|
||||
:: emit-assoc-start ( emitter event tag -- )
|
||||
event f tag f YAML_ANY_MAPPING_STYLE
|
||||
:: emit-assoc-start ( emitter event anchor tag -- )
|
||||
event anchor tag f YAML_ANY_MAPPING_STYLE
|
||||
yaml_mapping_start_event_initialize yaml-assert-ok
|
||||
emitter event yaml_emitter_emit yaml-assert-ok ;
|
||||
|
||||
|
@ -228,14 +312,14 @@ M: linked-assoc emit-value ( emitter event assoc -- )
|
|||
dup yaml_mapping_end_event_initialize yaml-assert-ok
|
||||
yaml_emitter_emit yaml-assert-ok ;
|
||||
|
||||
M: assoc emit-value ( emitter event seq -- )
|
||||
M: assoc emit-value ( emitter event anchor assoc -- )
|
||||
[ drop YAML_MAP_TAG emit-assoc-start ]
|
||||
[ emit-assoc ]
|
||||
[ drop emit-assoc-end ] 3tri ;
|
||||
M: set emit-value ( emitter event set -- )
|
||||
[ nip emit-assoc-body ]
|
||||
[ 2drop emit-assoc-end ] 4tri ;
|
||||
M: set emit-value ( emitter event anchor set -- )
|
||||
[ drop YAML_SET_TAG emit-assoc-start ]
|
||||
[ emit-set ]
|
||||
[ drop emit-assoc-end ] 3tri ;
|
||||
[ nip emit-set-body ]
|
||||
[ 2drop emit-assoc-end ] 4tri ;
|
||||
|
||||
! registers destructors (use with with-destructors)
|
||||
:: init-emitter ( -- emitter event )
|
||||
|
@ -259,7 +343,7 @@ M: set emit-value ( emitter event set -- )
|
|||
event f f f f yaml_document_start_event_initialize yaml-assert-ok
|
||||
emitter event yaml_emitter_emit yaml-assert-ok
|
||||
|
||||
emitter event obj emit-value
|
||||
emitter event obj emit-object
|
||||
|
||||
event f yaml_document_end_event_initialize yaml-assert-ok
|
||||
emitter event yaml_emitter_emit yaml-assert-ok ;
|
||||
|
@ -277,11 +361,11 @@ PRIVATE>
|
|||
: >yaml ( obj -- str )
|
||||
[
|
||||
[ init-emitter ] dip
|
||||
[ emit-doc ] [ drop flush-emitter ] 3bi
|
||||
[ replace-identities emit-doc ] [ drop flush-emitter ] 3bi
|
||||
] with-destructors ;
|
||||
|
||||
: >yaml-docs ( seq -- str )
|
||||
[
|
||||
[ init-emitter ] dip
|
||||
[ [ emit-doc ] with with each ] [ drop flush-emitter ] 3bi
|
||||
[ [ replace-identities emit-doc ] with with each ] [ drop flush-emitter ] 3bi
|
||||
] with-destructors ;
|
||||
|
|
Loading…
Reference in New Issue