1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
|
/*
FALCON - The Falcon Programming Language.
FILE: format_ext.cpp
The Format pretty print formatter class.
-------------------------------------------------------------------
Author: Giancarlo Niccolai
Begin: Thu, 14 Aug 2008 02:06:31 +0200
-------------------------------------------------------------------
(C) Copyright 2008: the FALCON developers (see list in AUTHORS file)
See LICENSE file for licensing details.
*/
#include "core_module.h"
#include <falcon/format.h>
#include <falcon/falconobject.h>
#include <falcon/fassert.h>
/*#
@beginmodule core
*/
namespace Falcon {
namespace core {
/*#
@class Format
@brief Controls the rendering of items into strings.
@ingroup general_purpose
@optparam fmtspec If provided, must be a valid format specifier
which is immediately parsed. In case of invalid format,
a ParseError is raised.
Format class is meant to provide an efficient way to format variables into
strings that can then be sent to output streams. Internally, the format class
is used in string expansion (the '\@' operator), but while string expansion causes
a string parsing to be initiated and an internal temporary Format object to be
instantiated each time an expansion is performed, using a prebuilt Format object
allows to optimize repeated formatting operations. Also, Format class instances may
be used as other objects properties, applied directly to strings being written on streams,
modified after being created and are generally more flexible than the string expansion.
The format specifier is a string that may contain various elements indicating how the target
variable should be rendered as a string.
@section format_specification Format specification
@b Size: The minimum field length; it can be just expressed by a number.
if the formatted output is wide as or wider than the allocated size, the output
will not be truncated, and the resulting string may be just too wide to be displayed
where it was intended to be. The size can be mandatory by adding '*' after it.
In this case, the format() method will return false (and eventually raise an error)
if the conversion caused the output to be wider than allowed.
@b Padding: the padding character is appended after the formatted size, or it is put in
front of it if alignment is to the right. To define padding character, use 'p' followed
by the character. In example, p0 to fill the field with zeros. Of course, the character
may be any Unicode character (the format string accepts standard Falcon character escapes).
In the special case of p0, front sign indicators are placed at the beginning of the field;
in example "4p0+" will produce "+001" "-002" and so on, while "4px+" will produce "xx+1", "xx-2" etc.
@b Numeric @b base: the way an integer should be rendered. It may be:
- Decimal: as it's the default translation, no command is needed; a 'N' character may
be added to the format to specify that we are actually expecting a number.
- Hexadecimal: Command may be 'x' (lowercase hex), 'X' (uppercase Hex), 'c'
(0x prefixed lowercase hex) or 'C' (0x prefixed uppercase hex). Binary: 'b' to
convert to binary, and 'B' to convert to binary and add a "b" after the number.
- Octal: 'o' to display an octal number, or '0' to display an octal with "0" prefix.
- Scientific: 'e' to display a number in scientific notation W.D+/-eM. Format of numbers in
scientific notation is fixed, so thousand separator and decimal digit separator cannot be set,
but decimals cipher setting will still work.
@b Decimals: a dot '.' followed by a number indicates the number of decimal to be displayed. If no
decimal is specified, floating point numbers will be displayed with all significant digits digits,
while if it's set to zero, decimal numbers will be rounded.
@b Decimal @b separator: a 'd' followed by any non-cipher character will be interpreted as decimal
separator setting. For example, to use central European standard for decimal numbers and limit the
output to 3 decimals, write ".3d,", or "d,.3". The default value is '.'.
@b (Thousands) @b Grouping: actually it's the integer part group separator, as it will be displayed
also for hexadecimal, octal and binary conversions. It is set using 'g' followed by the separator
character, it defaults to ','. Normally, it is not displayed; to activate it set also the integer
grouping digit count; normally is 3, but it's 4 in Japanese and Chinese locales, while it may be
useful to set it to 2 or 4 for hexadecimal, 3 for octal and 4 or 8 for binary. In example 'g4-'
would group digits 4 by 4, grouping them with a "-". Zero would disable grouping.
@b Grouping @b Character: If willing to change only the grouping character and not the default
grouping count, use 'G'.
@b Alignment: by default the field is aligned to the left; to align the field to
the right use 'r'.
@b Negative @b display @b format: By default, a '-' sign is appended in front of the number if it's
negative. If the '+' character is added to the format, then in case the number is positive, '+'
will be appended in front. '--' will postpend a '-' if the number is negative, while '++'
will postpend either '+' or '-' depending on the sign of the number. To display a parenthesis around
negative numbers, use '[', or use ']' to display a parenthesis for negative numbers and use the padding
character in front and after positive numbers. Using parenthesis will prevent using '+', '++' or '--'
formats. Format '-^' will add a - in front of padding space if the number is negative, while '+^'
will add plus or minus depending on number sign. In example, "5+" would render -12 as " -12", while "5+^"
will render as "- 12". If alignment is to the right, the sign will be added at the other side of the
padding: "5+^r" would render -12 as "12 -". If size is not mandatory, parenthesis will be wrapped
around the formatted field, while if size is mandatory they will be wrapped around the whole field,
included padding. In example "5[r" on -4 would render
as " (4)", while "5*[r" would render as "( 4)".
@b Nil @b format: How to represent a nil. It may be one of the following:
- 'nn': nil is not represented (mute).
- 'nN': nil is represented by "N"
- 'nl': nil is rendered with "nil"
- 'nL': nil is rendered with "Nil". This is also the default.
- 'nu': nil is rendered with "Null"
- 'nU': nil is rendered with "NULL"
- 'no': nil is rendered with "None"
- 'nA': nil is rendered with "NA"
@b Action @b on @b error: Normally, if trying to format something different from
what is expected, the method format() will simply return false. In example, to format
a string in a number, a string using the date formatter, a number in a simple
pad-and-size formatter etc. To change this behavior, use '/' followed by one of
the following:
- 'n': act as the wrong item was nil (and uses the defined nil formatter).
- '0': act as if the given item was 0, the empty string or an invalid date,
or anyhow the neuter item of the expected type.
- 'r': raise a type error.
A 'c' letter may be added after the '/' and before the specifier to try a
basic conversion into the expected type before triggering the requested effect.
In example, if the formatted item is an object and the conversion type is string
(that is, no numeric related options are set), this will cause the toString()
method of the target object to be called, or if not available, the toString()
function to be applied on the target object. In example "6/cr" tries to convert the
item to a 6 character long string, and if it fails (i.e. because toString() method
returns nil) an TypeError is raised.
@b Object @b specific @b format: Objects may accept an object specific formatting as
parameter of the standard toString() method. A pipe separator '|' will cause all the
following format to be passed unparsed to the toString() method of objects eventually
being formatted. If the object does not provides a toString() method, or if it's not
an object at all, an error will be raised. The object is the sole responsible for
parsing and applying its specific format.
@note Ranges will be represented as [n1:n2:n3] or [n1:] if they are open. Size, alignment and padding
will work on the whole range, while numeric formatting will be applied to each end of the range.
Example: the format specifier "8*Xs-g2" means to format variables in a field
of 8 characters, size mandatory (i.e. truncated if wider), Hexadecimal uppercase, grouped 2 by 2
with '-' characters. A result may be "0A-F1-DA".
Another example: "12.3'0r+/r" means to format a number in 12 ciphers, of which 3 are
fixed decimals, 0 padded, right aligned; a '+' is always added in front of positive
numbers. In case the formatted item is not a number, a type error is raised.
Format class instances may be applied on several variables; in example, a currency value
oriented numeric format may be applied on all the currency values of a program, and changing
the default format would just be a matter of changing just one format object.
*/
/*#
@method parse Format
@brief Initializes the Format instance with an optional value.
@param fmtspec Format specifier
@raise ParseError if the format specifier is not correct.
Sets or changes the format specifier for this Format instance.
If the format string is not correct, a ParseError is raised.
*/
FALCON_FUNC Format_parse ( ::Falcon::VMachine *vm )
{
CoreObject *einst = vm->self().asObject();
Format *fmt = (Format *) einst->getFalconData();
Item *param = vm->param( 0 );
if ( param != 0 )
{
if( ! param->isString() )
{
throw new ParamError( ErrorParam( e_inv_params ).extra( "[S]" ) );
}
else {
fmt->parse( *param->asString() );
if( ! fmt->isValid() )
{
throw new ParseError( ErrorParam( e_param_fmt_code ) );
}
}
}
}
FALCON_FUNC Format_init ( ::Falcon::VMachine *vm )
{
FalconObject *einst = static_cast<FalconObject*>( vm->self().asObject() );
Format *fmt = new Format;
einst->setUserData( fmt );
Format_parse( vm );
}
/*#
@method format Format
@brief Performs desired formatting on a target item.
@param item The item to be formatted
@optparam dest A string where to store the formatted data.
@return A formatted string
@raise ParamError if a format specifier has not been set yet.
@raise TypeError if the format specifier can't be applied the item because of
incompatible type.
Formats the variable as per the given format descriptor. If the class has been
instantiated without format, and the parse() method has not been called yet,
a ParamError is raised. If the type of the variable is incompatible with the
format descriptor, the method returns nil; a particular format specifier allows
to throw a TypeError in this case.
On success, the method returns a string containing a valid formatted representation
of the variable.
It is possible to provide a pre-allocated string where to store the formatted
result to improve performance and spare memory.
*/
FALCON_FUNC Format_format ( ::Falcon::VMachine *vm )
{
CoreObject *einst = vm->self().asObject();
Format *fmt = dyncast<Format*>( einst->getFalconData() );
Item *param = vm->param( 0 );
Item *dest = vm->param( 1 );
if( param == 0 || ( dest != 0 && ! dest->isString() ) )
{
throw new ParamError( ErrorParam( e_inv_params ).extra( "X,[S]" ) );
}
else
{
CoreString *tgt;
if( dest != 0 )
{
tgt = dest->asCoreString();
}
else {
tgt = new CoreString;
}
if( ! fmt->format( vm, *param, *tgt ) )
vm->retnil();
else
vm->retval( tgt );
}
}
/*#
@method toString Format
@brief Represents this format as a string.
@return The format specifier.
Returns a string representation of the format instance.
*/
FALCON_FUNC Format_toString ( ::Falcon::VMachine *vm )
{
FalconObject *einst = dyncast< FalconObject*>( vm->self().asObject() );
Format *fmt = dyncast< Format*>( einst->getFalconData() );
vm->retval( new CoreString( fmt->originalFormat()) );
}
}
}
/* end of format_ext.fal */
|