scratch

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 74  →  ?path2? @ 75
/bower_components/yaml.js/src/Inline.coffee
@@ -0,0 +1,487 @@
 
Pattern = require './Pattern'
Unescaper = require './Unescaper'
Escaper = require './Escaper'
Utils = require './Utils'
ParseException = require './Exception/ParseException'
DumpException = require './Exception/DumpException'
 
# Inline YAML parsing and dumping
class Inline
 
# Quoted string regular expression
@REGEX_QUOTED_STRING: '(?:"(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\']*(?:\'\'[^\']*)*)\')'
 
# Pre-compiled patterns
#
@PATTERN_TRAILING_COMMENTS: new Pattern '^\\s*#.*$'
@PATTERN_QUOTED_SCALAR: new Pattern '^'+@REGEX_QUOTED_STRING
@PATTERN_THOUSAND_NUMERIC_SCALAR: new Pattern '^(-|\\+)?[0-9,]+(\\.[0-9]+)?$'
@PATTERN_SCALAR_BY_DELIMITERS: {}
 
# Settings
@settings: {}
 
 
# Configure YAML inline.
#
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise
# @param [Function] objectDecoder A function to deserialize custom objects, null otherwise
#
@configure: (exceptionOnInvalidType = null, objectDecoder = null) ->
# Update settings
@settings.exceptionOnInvalidType = exceptionOnInvalidType
@settings.objectDecoder = objectDecoder
return
 
 
# Converts a YAML string to a JavaScript object.
#
# @param [String] value A YAML string
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise
# @param [Function] objectDecoder A function to deserialize custom objects, null otherwise
#
# @return [Object] A JavaScript object representing the YAML string
#
# @throw [ParseException]
#
@parse: (value, exceptionOnInvalidType = false, objectDecoder = null) ->
# Update settings from last call of Inline.parse()
@settings.exceptionOnInvalidType = exceptionOnInvalidType
@settings.objectDecoder = objectDecoder
 
if not value?
return ''
 
value = Utils.trim value
 
if 0 is value.length
return ''
 
# Keep a context object to pass through static methods
context = {exceptionOnInvalidType, objectDecoder, i: 0}
 
switch value.charAt(0)
when '['
result = @parseSequence value, context
++context.i
when '{'
result = @parseMapping value, context
++context.i
else
result = @parseScalar value, null, ['"', "'"], context
 
# Some comments are allowed at the end
if @PATTERN_TRAILING_COMMENTS.replace(value[context.i..], '') isnt ''
throw new ParseException 'Unexpected characters near "'+value[context.i..]+'".'
 
return result
 
 
# Dumps a given JavaScript variable to a YAML string.
#
# @param [Object] value The JavaScript variable to convert
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise
# @param [Function] objectEncoder A function to serialize custom objects, null otherwise
#
# @return [String] The YAML string representing the JavaScript object
#
# @throw [DumpException]
#
@dump: (value, exceptionOnInvalidType = false, objectEncoder = null) ->
if not value?
return 'null'
type = typeof value
if type is 'object'
if value instanceof Date
return value.toISOString()
else if objectEncoder?
result = objectEncoder value
if typeof result is 'string' or result?
return result
return @dumpObject value
if type is 'boolean'
return (if value then 'true' else 'false')
if Utils.isDigits(value)
return (if type is 'string' then "'"+value+"'" else String(parseInt(value)))
if Utils.isNumeric(value)
return (if type is 'string' then "'"+value+"'" else String(parseFloat(value)))
if type is 'number'
return (if value is Infinity then '.Inf' else (if value is -Infinity then '-.Inf' else (if isNaN(value) then '.NaN' else value)))
if Escaper.requiresDoubleQuoting value
return Escaper.escapeWithDoubleQuotes value
if Escaper.requiresSingleQuoting value
return Escaper.escapeWithSingleQuotes value
if '' is value
return '""'
if Utils.PATTERN_DATE.test value
return "'"+value+"'";
if value.toLowerCase() in ['null','~','true','false']
return "'"+value+"'"
# Default
return value;
 
 
# Dumps a JavaScript object to a YAML string.
#
# @param [Object] value The JavaScript object to dump
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise
# @param [Function] objectEncoder A function do serialize custom objects, null otherwise
#
# @return string The YAML string representing the JavaScript object
#
@dumpObject: (value, exceptionOnInvalidType, objectSupport = null) ->
# Array
if value instanceof Array
output = []
for val in value
output.push @dump val
return '['+output.join(', ')+']'
 
# Mapping
else
output = []
for key, val of value
output.push @dump(key)+': '+@dump(val)
return '{'+output.join(', ')+'}'
 
 
# Parses a scalar to a YAML string.
#
# @param [Object] scalar
# @param [Array] delimiters
# @param [Array] stringDelimiters
# @param [Object] context
# @param [Boolean] evaluate
#
# @return [String] A YAML string
#
# @throw [ParseException] When malformed inline YAML string is parsed
#
@parseScalar: (scalar, delimiters = null, stringDelimiters = ['"', "'"], context = null, evaluate = true) ->
unless context?
context = exceptionOnInvalidType: @settings.exceptionOnInvalidType, objectDecoder: @settings.objectDecoder, i: 0
{i} = context
 
if scalar.charAt(i) in stringDelimiters
# Quoted scalar
output = @parseQuotedScalar scalar, context
{i} = context
 
if delimiters?
tmp = Utils.ltrim scalar[i..], ' '
if not(tmp.charAt(0) in delimiters)
throw new ParseException 'Unexpected characters ('+scalar[i..]+').'
 
else
# "normal" string
if not delimiters
output = scalar[i..]
i += output.length
 
# Remove comments
strpos = output.indexOf ' #'
if strpos isnt -1
output = Utils.rtrim output[0...strpos]
 
else
joinedDelimiters = delimiters.join('|')
pattern = @PATTERN_SCALAR_BY_DELIMITERS[joinedDelimiters]
unless pattern?
pattern = new Pattern '^(.+?)('+joinedDelimiters+')'
@PATTERN_SCALAR_BY_DELIMITERS[joinedDelimiters] = pattern
if match = pattern.exec scalar[i..]
output = match[1]
i += output.length
else
throw new ParseException 'Malformed inline YAML string ('+scalar+').'
 
 
if evaluate
output = @evaluateScalar output, context
 
context.i = i
return output
 
 
# Parses a quoted scalar to YAML.
#
# @param [String] scalar
# @param [Object] context
#
# @return [String] A YAML string
#
# @throw [ParseException] When malformed inline YAML string is parsed
#
@parseQuotedScalar: (scalar, context) ->
{i} = context
 
unless match = @PATTERN_QUOTED_SCALAR.exec scalar[i..]
throw new ParseException 'Malformed inline YAML string ('+scalar[i..]+').'
 
output = match[0].substr(1, match[0].length - 2)
 
if '"' is scalar.charAt(i)
output = Unescaper.unescapeDoubleQuotedString output
else
output = Unescaper.unescapeSingleQuotedString output
 
i += match[0].length
 
context.i = i
return output
 
 
# Parses a sequence to a YAML string.
#
# @param [String] sequence
# @param [Object] context
#
# @return [String] A YAML string
#
# @throw [ParseException] When malformed inline YAML string is parsed
#
@parseSequence: (sequence, context) ->
output = []
len = sequence.length
{i} = context
i += 1
 
# [foo, bar, ...]
while i < len
context.i = i
switch sequence.charAt(i)
when '['
# Nested sequence
output.push @parseSequence sequence, context
{i} = context
when '{'
# Nested mapping
output.push @parseMapping sequence, context
{i} = context
when ']'
return output
when ',', ' ', "\n"
# Do nothing
else
isQuoted = (sequence.charAt(i) in ['"', "'"])
value = @parseScalar sequence, [',', ']'], ['"', "'"], context
{i} = context
 
if not(isQuoted) and typeof(value) is 'string' and (value.indexOf(': ') isnt -1 or value.indexOf(":\n") isnt -1)
# Embedded mapping?
try
value = @parseMapping '{'+value+'}'
catch e
# No, it's not
 
 
output.push value
 
--i
 
++i
 
throw new ParseException 'Malformed inline YAML string '+sequence
 
 
# Parses a mapping to a YAML string.
#
# @param [String] mapping
# @param [Object] context
#
# @return [String] A YAML string
#
# @throw [ParseException] When malformed inline YAML string is parsed
#
@parseMapping: (mapping, context) ->
output = {}
len = mapping.length
{i} = context
i += 1
 
# {foo: bar, bar:foo, ...}
shouldContinueWhileLoop = false
while i < len
context.i = i
switch mapping.charAt(i)
when ' ', ',', "\n"
++i
context.i = i
shouldContinueWhileLoop = true
when '}'
return output
 
if shouldContinueWhileLoop
shouldContinueWhileLoop = false
continue
 
# Key
key = @parseScalar mapping, [':', ' ', "\n"], ['"', "'"], context, false
{i} = context
 
# Value
done = false
 
while i < len
context.i = i
switch mapping.charAt(i)
when '['
# Nested sequence
value = @parseSequence mapping, context
{i} = context
# Spec: Keys MUST be unique; first one wins.
# Parser cannot abort this mapping earlier, since lines
# are processed sequentially.
if output[key] == undefined
output[key] = value
done = true
when '{'
# Nested mapping
value = @parseMapping mapping, context
{i} = context
# Spec: Keys MUST be unique; first one wins.
# Parser cannot abort this mapping earlier, since lines
# are processed sequentially.
if output[key] == undefined
output[key] = value
done = true
when ':', ' ', "\n"
# Do nothing
else
value = @parseScalar mapping, [',', '}'], ['"', "'"], context
{i} = context
# Spec: Keys MUST be unique; first one wins.
# Parser cannot abort this mapping earlier, since lines
# are processed sequentially.
if output[key] == undefined
output[key] = value
done = true
--i
 
++i
 
if done
break
 
throw new ParseException 'Malformed inline YAML string '+mapping
 
 
# Evaluates scalars and replaces magic values.
#
# @param [String] scalar
#
# @return [String] A YAML string
#
@evaluateScalar: (scalar, context) ->
scalar = Utils.trim(scalar)
scalarLower = scalar.toLowerCase()
 
switch scalarLower
when 'null', '', '~'
return null
when 'true'
return true
when 'false'
return false
when '.inf'
return Infinity
when '.nan'
return NaN
when '-.inf'
return Infinity
else
firstChar = scalarLower.charAt(0)
switch firstChar
when '!'
firstSpace = scalar.indexOf(' ')
if firstSpace is -1
firstWord = scalarLower
else
firstWord = scalarLower[0...firstSpace]
switch firstWord
when '!'
if firstSpace isnt -1
return parseInt @parseScalar(scalar[2..])
return null
when '!str'
return Utils.ltrim scalar[4..]
when '!!str'
return Utils.ltrim scalar[5..]
when '!!int'
return parseInt(@parseScalar(scalar[5..]))
when '!!bool'
return Utils.parseBoolean(@parseScalar(scalar[6..]), false)
when '!!float'
return parseFloat(@parseScalar(scalar[7..]))
when '!!timestamp'
return Utils.stringToDate(Utils.ltrim(scalar[11..]))
else
unless context?
context = exceptionOnInvalidType: @settings.exceptionOnInvalidType, objectDecoder: @settings.objectDecoder, i: 0
{objectDecoder, exceptionOnInvalidType} = context
 
if objectDecoder
# If objectDecoder function is given, we can do custom decoding of custom types
trimmedScalar = Utils.rtrim scalar
firstSpace = trimmedScalar.indexOf(' ')
if firstSpace is -1
return objectDecoder trimmedScalar, null
else
subValue = Utils.ltrim trimmedScalar[firstSpace+1..]
unless subValue.length > 0
subValue = null
return objectDecoder trimmedScalar[0...firstSpace], subValue
 
if exceptionOnInvalidType
throw new ParseException 'Custom object support when parsing a YAML file has been disabled.'
 
return null
when '0'
if '0x' is scalar[0...2]
return Utils.hexDec scalar
else if Utils.isDigits scalar
return Utils.octDec scalar
else if Utils.isNumeric scalar
return parseFloat scalar
else
return scalar
when '+'
if Utils.isDigits scalar
raw = scalar
cast = parseInt(raw)
if raw is String(cast)
return cast
else
return raw
else if Utils.isNumeric scalar
return parseFloat scalar
else if @PATTERN_THOUSAND_NUMERIC_SCALAR.test scalar
return parseFloat(scalar.replace(',', ''))
return scalar
when '-'
if Utils.isDigits(scalar[1..])
if '0' is scalar.charAt(1)
return -Utils.octDec(scalar[1..])
else
raw = scalar[1..]
cast = parseInt(raw)
if raw is String(cast)
return -cast
else
return -raw
else if Utils.isNumeric scalar
return parseFloat scalar
else if @PATTERN_THOUSAND_NUMERIC_SCALAR.test scalar
return parseFloat(scalar.replace(',', ''))
return scalar
else
if date = Utils.stringToDate(scalar)
return date
else if Utils.isNumeric(scalar)
return parseFloat scalar
else if @PATTERN_THOUSAND_NUMERIC_SCALAR.test scalar
return parseFloat(scalar.replace(',', ''))
return scalar
 
module.exports = Inline