1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4066 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
why 2003-07-11 22:52:14 +00:00
parent 30399f6c75
commit a1e257ec48
9 changed files with 359 additions and 271 deletions

View file

@ -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.

View file

@ -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 */
@ -1015,6 +1016,162 @@ syck_node_transform( self )
return rb_funcall( oDefaultLoader, s_transfer, 2, type_id, t );
}
/*
* 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
*/
@ -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 );
}

View file

@ -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 );

View file

@ -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;
@ -192,6 +203,82 @@ struct _syck_parser {
void *bonus;
};
/*
* 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
*/
@ -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 );

View file

@ -12,15 +12,26 @@ 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
#
@ -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'

View file

@ -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

View file

@ -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?
"''"

View file

@ -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--- "

View file

@ -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