diff options
-rw-r--r-- | ChangeLog | 16 | ||||
-rw-r--r-- | ext/syck/rubyext.c | 173 | ||||
-rw-r--r-- | ext/syck/syck.c | 37 | ||||
-rw-r--r-- | ext/syck/syck.h | 104 | ||||
-rw-r--r-- | lib/yaml.rb | 37 | ||||
-rw-r--r-- | lib/yaml/emitter.rb | 245 | ||||
-rw-r--r-- | lib/yaml/rubytypes.rb | 8 | ||||
-rw-r--r-- | lib/yaml/stream.rb | 2 | ||||
-rw-r--r-- | lib/yaml/syck.rb | 8 |
9 files changed, 359 insertions, 271 deletions
@@ -1,3 +1,19 @@ +Sat Jul 12 04:43:57 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net> + + * ext/syck/emitter.c: new emitter code. + + * ext/syck/rubyext.c: Emitter class. + + * lib/yaml.rb: Load Syck emitter, if available. + + * lib/yaml/stream.rb: ditto. + + * lib/yaml/baseemitter.rb: underlying class for all emitters. + + * lib/yaml/rubytypes.rb: use BaseEmitter abstraction. + + * lib/yaml/emitter.rb: ditto. + Sat Jul 12 01:21:54 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net> * eval.c (avalue_to_svalue): typo. diff --git a/ext/syck/rubyext.c b/ext/syck/rubyext.c index 724dd7a912..6a26cd45f8 100644 --- a/ext/syck/rubyext.c +++ b/ext/syck/rubyext.c @@ -40,7 +40,7 @@ typedef struct RVALUE { static ID s_new, s_utc, s_at, s_to_f, s_read, s_binmode, s_call, s_transfer, s_update, s_dup, s_match; static VALUE sym_model, sym_generic; static VALUE sym_scalar, sym_seq, sym_map; -VALUE cDate, cParser, cLoader, cNode, cPrivateType, cDomainType, cBadAlias, cMergeKey; +VALUE cDate, cParser, cLoader, cNode, cPrivateType, cDomainType, cBadAlias, cMergeKey, cEmitter; VALUE oDefaultLoader; /* @@ -57,6 +57,7 @@ SYMID rb_syck_parse_handler _((SyckParser *, SyckNode *)); SYMID rb_syck_load_handler _((SyckParser *, SyckNode *)); void rb_syck_err_handler _((SyckParser *, char *)); SyckNode * rb_syck_bad_anchor_handler _((SyckParser *, char *)); +void rb_syck_output_handler _((SyckEmitter *, char *, long)); struct parser_xtra { VALUE data; /* Borrowed this idea from marshal.c to fix [ruby-core:8067] problem */ @@ -1016,6 +1017,162 @@ syck_node_transform( self ) } /* + * Handle output from the emitter + */ +void +rb_syck_output_handler( emitter, str, len ) + SyckEmitter *emitter; + char *str; + long len; +{ + rb_str_cat( (VALUE)emitter->bonus, str, len ); +} + +/* + * Mark emitter values. + */ +static void +syck_mark_emitter(emitter) + SyckEmitter *emitter; +{ + rb_gc_mark(emitter->ignore_id); +} + +/* + * YAML::Syck::Emitter.new + */ +VALUE +syck_emitter_new(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE pobj, options, init_argv[1]; + SyckEmitter *emitter = syck_new_emitter(); + syck_emitter_ignore_id( emitter, Qnil ); + syck_emitter_handler( emitter, rb_syck_output_handler ); + + emitter->bonus = (void *)rb_str_new2( "" ); + + rb_scan_args(argc, argv, "01", &options); + pobj = Data_Wrap_Struct( class, syck_mark_emitter, syck_free_emitter, emitter ); + + if ( ! rb_obj_is_instance_of( options, rb_cHash ) ) + { + options = rb_hash_new(); + } + init_argv[0] = options; + rb_obj_call_init(pobj, 1, init_argv); + return pobj; +} + +/* + * YAML::Syck::Emitter.initialize( options ) + */ +static VALUE +syck_emitter_initialize( self, options ) + VALUE self, options; +{ + rb_iv_set(self, "@options", options); + return self; +} + +/* + * YAML::Syck::Emitter.level + */ +VALUE +syck_emitter_level_m( self ) + VALUE self; +{ + SyckEmitter *emitter; + + Data_Get_Struct(self, SyckEmitter, emitter); + return LONG2NUM( emitter->level ); +} + +/* + * YAML::Syck::Emitter.flush + */ +VALUE +syck_emitter_flush_m( self ) + VALUE self; +{ + SyckEmitter *emitter; + + Data_Get_Struct(self, SyckEmitter, emitter); + syck_emitter_flush( emitter ); + return self; +} + +/* + * YAML::Syck::Emitter.write( str ) + */ +VALUE +syck_emitter_write_m( self, str ) + VALUE str; +{ + SyckEmitter *emitter; + + Data_Get_Struct(self, SyckEmitter, emitter); + syck_emitter_write( emitter, RSTRING(str)->ptr, RSTRING(str)->len ); + return self; +} + +/* + * YAML::Syck::Emitter.simple( str ) + */ +VALUE +syck_emitter_simple_write( self, str ) + VALUE str; +{ + SyckEmitter *emitter; + + Data_Get_Struct(self, SyckEmitter, emitter); + syck_emitter_simple( emitter, RSTRING(str)->ptr, RSTRING(str)->len ); + return self; +} + +/* + * YAML::Syck::Emitter.start_object( object_id ) + */ +VALUE +syck_emitter_start_object( self, oid ) + VALUE self, oid; +{ + char *anchor_name; + SyckEmitter *emitter; + + Data_Get_Struct(self, SyckEmitter, emitter); + anchor_name = syck_emitter_start_obj( emitter, oid ); + + if ( anchor_name == NULL ) + { + return Qnil; + } + + return rb_str_new2( anchor_name ); +} + +/* + * YAML::Syck::Emitter.end_object( object_id ) + */ +VALUE +syck_emitter_end_object( self, oid ) + VALUE self, oid; +{ + SyckEmitter *emitter; + + Data_Get_Struct(self, SyckEmitter, emitter); + syck_emitter_end_obj( emitter ); + + if ( emitter->level < 0 ) + { + syck_emitter_flush( emitter ); + } + return (VALUE)emitter->bonus; +} + +/* * Initialize Syck extension */ void @@ -1118,5 +1275,19 @@ Init_syck() * Define YAML::Syck::MergeKey class */ cMergeKey = rb_define_class_under( rb_syck, "MergeKey", rb_cObject ); + + /* + * Define YAML::Syck::Emitter class + */ + cEmitter = rb_define_class_under( rb_syck, "Emitter", rb_cObject ); + rb_define_singleton_method( cEmitter, "new", syck_emitter_new, -1 ); + rb_define_method( cEmitter, "initialize", syck_emitter_initialize, 1 ); + rb_define_method( cEmitter, "level", syck_emitter_level_m, 0 ); + rb_define_method( cEmitter, "write", syck_emitter_write_m, 1 ); + rb_define_method( cEmitter, "<<", syck_emitter_write_m, 1 ); + rb_define_method( cEmitter, "simple", syck_emitter_simple_write, 1 ); + rb_define_method( cEmitter, "flush", syck_emitter_flush_m, 0 ); + rb_define_method( cEmitter, "start_object", syck_emitter_start_object, 1 ); + rb_define_method( cEmitter, "end_object", syck_emitter_end_object, 0 ); } diff --git a/ext/syck/syck.c b/ext/syck/syck.c index a7955b448b..4b7be32a18 100644 --- a/ext/syck/syck.c +++ b/ext/syck/syck.c @@ -11,10 +11,6 @@ #include "syck.h" -#define SYCK_YAML_MAJOR 1 -#define SYCK_YAML_MINOR 0 -#define SYCK_BUFFERSIZE 262144 - void syck_parser_pop_level( SyckParser * ); /* @@ -30,6 +26,9 @@ syck_assert( char *file_name, unsigned line_num ) abort(); } +/* + * Allocates and copies a string + */ char * syck_strndup( char *buf, long len ) { @@ -40,7 +39,7 @@ syck_strndup( char *buf, long len ) } /* - * Default IO functions + * Default FILE IO function */ long syck_io_file_read( char *buf, SyckIoFile *file, long max_size, long skip ) @@ -52,18 +51,15 @@ syck_io_file_read( char *buf, SyckIoFile *file, long max_size, long skip ) max_size -= skip; len = fread( buf + skip, max_size, sizeof( char ), file->ptr ); -#if REDEBUG - printf( "LEN: %d\n", len ); -#endif len += skip; buf[len] = '\0'; -#if REDEBUG - printf( "POS: %d\n", len ); - printf( "BUFFER: %s\n", buf ); -#endif + return len; } +/* + * Default string IO function + */ long syck_io_str_read( char *buf, SyckIoStr *str, long max_size, long skip ) { @@ -95,15 +91,9 @@ syck_io_str_read( char *buf, SyckIoStr *str, long max_size, long skip ) len = str->ptr - beg; S_MEMCPY( buf + skip, beg, char, len ); } -#if REDEBUG - printf( "LEN: %d\n", len ); -#endif len += skip; buf[len] = '\0'; -#if REDEBUG - printf( "POS: %d\n", len ); - printf( "BUFFER: %s\n", buf ); -#endif + return len; } @@ -150,6 +140,9 @@ syck_parser_reset_cursor( SyckParser *p ) p->force_token = 0; } +/* + * Value to return on a parse error + */ void syck_parser_set_root_on_error( SyckParser *p, SYMID roer ) { @@ -212,7 +205,7 @@ syck_st_free( SyckParser *p ) { /* * Free the adhoc symbol table - */ + */ if ( p->syms != NULL ) { st_free_table( p->syms ); @@ -410,10 +403,6 @@ syck_move_tokens( SyckParser *p ) if ( skip < 1 ) return 0; -#if REDEBUG - printf( "DIFF: %d\n", skip ); -#endif - if ( ( count = p->token - p->buffer ) ) { S_MEMMOVE( p->buffer, p->token, char, skip ); diff --git a/ext/syck/syck.h b/ext/syck/syck.h index 20ad18a843..259a4cdfbd 100644 --- a/ext/syck/syck.h +++ b/ext/syck/syck.h @@ -10,6 +10,9 @@ #ifndef SYCK_H #define SYCK_H +#define SYCK_YAML_MAJOR 1 +#define SYCK_YAML_MINOR 0 + #define SYCK_VERSION "0.35" #define YAML_DOMAIN "yaml.org,2002" @@ -43,6 +46,7 @@ extern "C" { #endif #define ALLOC_CT 8 +#define SYCK_BUFFERSIZE 262144 #define S_ALLOC_N(type,n) (type*)malloc(sizeof(type)*(n)) #define S_ALLOC(type) (type*)malloc(sizeof(type)) #define S_REALLOC_N(var,type,n) (var)=(type*)realloc((char*)(var),sizeof(type)*(n)) @@ -66,11 +70,7 @@ extern "C" { */ #define SYMID unsigned long -typedef struct _syck_parser SyckParser; -typedef struct _syck_file SyckIoFile; -typedef struct _syck_str SyckIoStr; typedef struct _syck_node SyckNode; -typedef struct _syck_level SyckLevel; enum syck_kind_tag { syck_map_kind, @@ -83,6 +83,9 @@ enum map_part { map_value }; +/* + * Node metadata struct + */ struct _syck_node { /* Symbol table ID */ SYMID id; @@ -119,6 +122,11 @@ struct _syck_node { /* * Parser definitions */ +typedef struct _syck_parser SyckParser; +typedef struct _syck_file SyckIoFile; +typedef struct _syck_str SyckIoStr; +typedef struct _syck_level SyckLevel; + typedef SYMID (*SyckNodeHandler)(SyckParser *, SyckNode *); typedef void (*SyckErrorHandler)(SyckParser *, char *); typedef SyckNode * (*SyckBadAnchorHandler)(SyckParser *, char *); @@ -142,6 +150,9 @@ enum syck_level_status { syck_lvl_pause }; +/* + * Parser struct + */ struct _syck_parser { /* Root node */ SYMID root, root_on_error; @@ -193,6 +204,82 @@ struct _syck_parser { }; /* + * Emitter definitions + */ +typedef struct _syck_emitter SyckEmitter; +typedef struct _syck_emitter_node SyckEmitterNode; + +typedef void (*SyckOutputHandler)(SyckEmitter *, char *, long); + +enum doc_stage { + doc_open, + doc_need_header, + doc_processing +}; + +enum block_styles { + block_arbitrary, + block_fold, + block_literal +}; + +/* + * Emitter struct + */ +struct _syck_emitter { + /* Headerless doc flag */ + int headless; + /* Sequence map shortcut flag */ + int seq_map; + /* Force header? */ + int use_header; + /* Force version? */ + int use_version; + /* Sort hash keys */ + int sort_keys; + /* Anchor format */ + char *anchor_format; + /* Explicit typing on all collections? */ + int explicit_typing; + /* Best width on folded scalars */ + int best_width; + /* Use literal[1] or folded[2] blocks on all text? */ + enum block_styles block_style; + /* Stage of written document */ + enum doc_stage stage; + /* Level counter */ + int level; + /* Default indentation */ + int indent; + /* Object ignore ID */ + SYMID ignore_id; + /* Symbol table for anchors */ + st_table *markers, *anchors; + /* Custom buffer size */ + size_t bufsize; + /* Buffer */ + char *buffer, *marker; + /* Absolute position of the buffer */ + long bufpos; + /* Handler for output */ + SyckOutputHandler handler; + /* Pointer for extension's use */ + void *bonus; +}; + +/* + * Emitter node metadata struct + */ +struct _syck_emitter_node { + /* Node buffer position */ + long pos; + /* Current indent */ + long indent; + /* Collection? */ + int is_shortcut; +}; + +/* * Handler prototypes */ SYMID syck_hdlr_add_node( SyckParser *, SyckNode * ); @@ -215,6 +302,15 @@ char *syck_match_implicit( char *, size_t ); char *syck_strndup( char *, long ); long syck_io_file_read( char *, SyckIoFile *, long, long ); long syck_io_str_read( char *, SyckIoStr *, long, long ); +SyckEmitter *syck_new_emitter(); +void syck_emitter_ignore_id( SyckEmitter *, SYMID ); +void syck_emitter_handler( SyckEmitter *, SyckOutputHandler ); +void syck_free_emitter( SyckEmitter * ); +void syck_emitter_clear( SyckEmitter * ); +void syck_emitter_write( SyckEmitter *, char *, long ); +void syck_emitter_flush( SyckEmitter * ); +char *syck_emitter_start_obj( SyckEmitter *, SYMID ); +void syck_emitter_end_obj( SyckEmitter * ); SyckParser *syck_new_parser(); void syck_free_parser( SyckParser * ); void syck_parser_set_root_on_error( SyckParser *, SYMID ); diff --git a/lib/yaml.rb b/lib/yaml.rb index 39e176541e..d33f2e202a 100644 --- a/lib/yaml.rb +++ b/lib/yaml.rb @@ -12,18 +12,29 @@ module YAML require 'yaml/syck' @@parser = YAML::Syck::Parser @@loader = YAML::Syck::DefaultLoader + @@emitter = YAML::Syck::Emitter rescue LoadError require 'yaml/parser' @@parser = YAML::Parser @@loader = YAML::DefaultLoader + require 'yaml/emitter' + @@emitter = YAML::Emitter end - require 'yaml/emitter' require 'yaml/loader' require 'yaml/stream' # # Load a single document from the current stream # + def YAML.dump( obj, io = nil ) + io ||= "" + io << obj.to_yaml + io + end + + # + # Load a single document from the current stream + # def YAML.load( io ) yp = @@parser.new.load( io ) end @@ -158,6 +169,30 @@ module YAML end end + # + # Allocate an Emitter if needed + # + def YAML.quick_emit( oid, opts = {}, &e ) + old_opt = nil + if opts[:Emitter].is_a? @@emitter + out = opts.delete( :Emitter ) + old_opt = out.options.dup + out.options.update( opts ) + else + out = @@emitter.new( opts ) + end + aidx = out.start_object( oid ) + if aidx + out.simple( "*#{ aidx }" ) + else + e.call( out ) + end + if old_opt.is_a? Hash + out.options = old_opt + end + out.end_object + end + end require 'yaml/rubytypes' diff --git a/lib/yaml/emitter.rb b/lib/yaml/emitter.rb index 0255cc7d02..a6be4a6733 100644 --- a/lib/yaml/emitter.rb +++ b/lib/yaml/emitter.rb @@ -2,9 +2,8 @@ # Output classes and methods # -require 'yaml/constants' +require 'yaml/baseemitter' require 'yaml/encoding' -require 'yaml/error' module YAML @@ -13,7 +12,11 @@ module YAML # class Emitter + + include BaseEmitter + attr_accessor :options + def initialize( opts ) opts = {} if opts.class != Hash @options = YAML::DEFAULTS.dup.update( opts ) @@ -30,6 +33,10 @@ module YAML @buffer = [] end + def level + @level + end + # # Version string # @@ -48,201 +55,6 @@ module YAML end end - # - # Emit binary data - # - def binary_base64( value ) - self << "!binary " - self.node_text( [value].pack("m"), '|' ) - end - - # - # Emit plain, normal flowing text - # - def node_text( value, block = '>' ) - @seq_map = false - valx = value.dup - if @options[:UseBlock] - block = '|' - elsif not @options[:UseFold] and valx =~ /\n[ \t]/ and not valx =~ /#{YAML::ESCAPE_CHAR}/ - block = '|' - end - str = block.dup - if valx =~ /\n\Z\n/ - str << "+" - elsif valx =~ /\Z\n/ - else - str << "-" - end - if valx =~ /#{YAML::ESCAPE_CHAR}/ - valx = YAML::escape( valx ) - end - if valx =~ /\A[ \t#]/ - str << @options[:Indent].to_s - end - if block == '>' - valx = fold( valx ) - end - self << str + indent_text( valx ) + "\n" - end - - # - # Emit a simple, unqouted string - # - def simple( value ) - @seq_map = false - self << value.to_s - end - - # - # Emit double-quoted string - # - def double( value ) - "\"#{YAML.escape( value )}\"" - end - - # - # Emit single-quoted string - # - def single( value ) - "'#{value}'" - end - - # - # Write a text block with the current indent - # - def indent_text( text ) - return "" if text.to_s.empty? - spacing = " " * ( @level * @options[:Indent] ) - return "\n" + text.gsub( /^([^\n])/, "#{spacing}\\1" ) - end - - # - # Write a current indent - # - def indent - #p [ self.id, @level, :INDENT ] - return " " * ( @level * @options[:Indent] ) - end - - # - # Add indent to the buffer - # - def indent! - self << indent - end - - # - # Folding paragraphs within a column - # - def fold( value ) - value.gsub!( /\A\n+/, '' ) - folded = $&.to_s - width = (0..@options[:BestWidth]) - while not value.empty? - last = value.index( /(\n+)/ ) - chop_s = false - if width.include?( last ) - last += $1.length - 1 - elsif width.include?( value.length ) - last = value.length - else - last = value.rindex( /[ \t]/, @options[:BestWidth] ) - chop_s = true - end - folded += value.slice!( 0, width.include?( last ) ? last + 1 : @options[:BestWidth] ) - folded.chop! if chop_s - folded += "\n" unless value.empty? - end - folded - end - - # - # Quick mapping - # - def map( type, &e ) - val = Mapping.new - e.call( val ) - self << "#{type} " if type.length.nonzero? - - # - # Empty hashes - # - if val.length.zero? - self << "{}" - @seq_map = false - else - if @buffer.length == 1 and @options[:UseHeader] == false and type.length.zero? - @headless = 1 - end - - defkey = @options.delete( :DefaultKey ) - if defkey - seq_map_shortcut - self << "= : " - defkey.to_yaml( :Emitter => self ) - end - - # - # Emit the key and value - # - val.each { |v| - seq_map_shortcut - if v[0].is_complex_yaml? - self << "? " - end - v[0].to_yaml( :Emitter => self ) - if v[0].is_complex_yaml? - self << "\n" - indent! - end - self << ": " - v[1].to_yaml( :Emitter => self ) - } - end - end - - def seq_map_shortcut - if @seq_map - @anchor_extras[@buffer.length - 1] = "\n" + indent - @seq_map = false - else - self << "\n" - indent! - end - end - - # - # Quick sequence - # - def seq( type, &e ) - @seq_map = false - val = Sequence.new - e.call( val ) - self << "#{type} " if type.length.nonzero? - - # - # Empty arrays - # - if val.length.zero? - self << "[]" - else - if @buffer.length == 1 and @options[:UseHeader] == false and type.length.zero? - @headless = 1 - end - # - # Emit the key and value - # - val.each { |v| - self << "\n" - indent! - self << "- " - @seq_map = true if v.class == Hash - v.to_yaml( :Emitter => self ) - } - end - end - # # Concatenate to the buffer # @@ -291,44 +103,5 @@ module YAML end end - # - # Emitter helper classes - # - class Mapping < Array - def add( k, v ) - push [k, v] - end - end - - class Sequence < Array - def add( v ) - push v - end - end - - # - # Allocate an Emitter if needed - # - def YAML.quick_emit( oid, opts = {}, &e ) - old_opt = nil - if opts[:Emitter].is_a? YAML::Emitter - out = opts.delete( :Emitter ) - old_opt = out.options.dup - out.options.update( opts ) - else - out = YAML::Emitter.new( opts ) - end - aidx = out.start_object( oid ) - if aidx - out.simple( "*#{out.options[:AnchorFormat]} " % [ aidx ] ) - else - e.call( out ) - end - if old_opt.is_a? Hash - out.options = old_opt - end - out.end_object - end - end diff --git a/lib/yaml/rubytypes.rb b/lib/yaml/rubytypes.rb index 09aab98895..9f661b0b6c 100644 --- a/lib/yaml/rubytypes.rb +++ b/lib/yaml/rubytypes.rb @@ -53,14 +53,14 @@ class Hash opts[:DocType] = self.class if Hash === opts YAML::quick_emit( self.object_id, opts ) { |out| hash_type = to_yaml_type - if not out.options[:ExplicitTypes] and hash_type == "!map" + if not out.options(:ExplicitTypes) and hash_type == "!map" hash_type = "" end out.map( hash_type ) { |map| # # Sort the hash # - if out.options[:SortKeys] + if out.options(:SortKeys) map.concat( self.sort ) else map.concat( self.to_a ) @@ -213,7 +213,7 @@ class Array opts[:DocType] = self.class if Hash === opts YAML::quick_emit( self.object_id, opts ) { |out| array_type = to_yaml_type - if not out.options[:ExplicitTypes] and array_type == "!seq" + if not out.options(:ExplicitTypes) and array_type == "!seq" array_type = "" end @@ -302,7 +302,7 @@ class String end end if not complex - ostr = if out.options[:KeepValue] + ostr = if out.options(:KeepValue) self elsif empty? "''" diff --git a/lib/yaml/stream.rb b/lib/yaml/stream.rb index 3b6919b5dd..943c51526b 100644 --- a/lib/yaml/stream.rb +++ b/lib/yaml/stream.rb @@ -28,7 +28,7 @@ module YAML opts = @options.dup opts[:UseHeader] = true if @documents.length > 1 ct = 0 - out = Emitter.new( opts ) + out = YAML::Syck::Emitter.new( opts ) @documents.each { |v| if ct > 0 out << "\n--- " diff --git a/lib/yaml/syck.rb b/lib/yaml/syck.rb index faf57e8036..267067feb5 100644 --- a/lib/yaml/syck.rb +++ b/lib/yaml/syck.rb @@ -4,6 +4,7 @@ # require 'syck' require 'yaml/basenode' +require 'yaml/baseemitter' module YAML module Syck @@ -15,5 +16,12 @@ module YAML include YAML::BaseNode end + # + # Mixin BaseEmitter functionality + # + class Emitter + include YAML::BaseEmitter + end + end end |