JavaScript Python PHP
import * as __biblioteca0 from "yaml";
function executar(input = 'nome: João 😊\ncodigo: "00123"\nativo: true\nvalor: null') {
const stringify = __biblioteca0["stringify"];
const LineCounter = __biblioteca0["LineCounter"];
const parseDocument = __biblioteca0["parseDocument"];
const isAlias = __biblioteca0["isAlias"];
const isScalar = __biblioteca0["isScalar"];
const isSeq = __biblioteca0["isSeq"];
const isMap = __biblioteca0["isMap"];
const SAFE_TAGS = new Set(['str', 'bool', 'null', 'int', 'float', 'map', 'seq'].map(tag => `tag:yaml.org,2002:${tag}`));
function parseJsonCompatible(input, format, onObjectKeys) {
if (!input.trim())
throw new Error(`Informe um documento ${format}.`);
if (input.length > 1000000)
throw new Error('A entrada excede o limite de 1 milhão de caracteres.');
if (format === 'JSON') {
// Syntax check only: do not use the rounded numeric values from JSON.parse.
try {
JSON.parse(input);
}
catch (cause) {
throw new Error(`JSON inválido: ${cause instanceof Error ? cause.message : 'erro de sintaxe'}`);
}
}
const lines = new LineCounter();
const doc = parseDocument(input, { version: '1.2', schema: format === 'JSON' ? 'json' : 'core', intAsBigInt: true, uniqueKeys: true, lineCounter: lines, prettyErrors: true, resolveKnownTags: false });
const issue = doc.errors[0] ?? doc.warnings[0];
if (issue)
throw new Error(`${format} inválido (${issue.code}): ${issue.message}`);
if (doc.directives?.yaml.version !== '1.2')
throw new Error('Somente YAML 1.2 é aceito para preservar os tipos de JSON.');
let count = 0;
function fail(message, node) {
const offset = node?.range?.[0];
const position = offset === undefined ? '' : (() => { const { line, col } = lines.linePos(offset); return ` (linha ${line}, coluna ${col})`; })();
throw new Error(message + position);
}
function convert(node, depth = 0) {
if (++count > 100000 || depth > 100)
fail('Documento muito complexo: limite de 100 níveis ou 100 mil nós.', node);
if (!node)
return null;
if (isAlias(node))
fail('Aliases YAML não são aceitos: JSON não preserva referências ou ciclos.', node);
if (node.tag && !SAFE_TAGS.has(node.tag))
fail(`Tag não permitida: ${node.tag}.`, node);
if (isScalar(node)) {
const value = node.value;
if (typeof value === 'bigint') {
if (value === BigInt(0) && node.source?.startsWith('-'))
fail('JSON de saída não preserva o sinal de zero negativo. Use 0 ou uma string.', node);
if (value > BigInt(Number.MAX_SAFE_INTEGER) || value < BigInt(Number.MIN_SAFE_INTEGER))
fail('Inteiro fora do intervalo seguro de JSON/JavaScript. Use uma string entre aspas para preservar todos os dígitos.', node);
return Number(value);
}
if (typeof value === 'number') {
if (!Number.isFinite(value))
fail('JSON não representa NaN ou infinito.', node);
if (Number.isInteger(value) && !Number.isSafeInteger(value))
fail('Número inteiro fora do intervalo seguro. Use uma string entre aspas para preservar a precisão.', node);
if (value === 0 && /[1-9]/.test((node.source ?? '').split(/[eE]/)[0]))
fail('Número pequeno demais para a precisão de JavaScript. Use uma string entre aspas.', node);
if (Object.is(value, -0))
fail('JSON de saída não preserva o sinal de zero negativo. Use 0 ou uma string.', node);
return value;
}
if (value === null || typeof value === 'string' || typeof value === 'boolean')
return value;
fail('Valor não representável em JSON.', node);
}
if (isSeq(node))
return node.items.map(item => convert(item, depth + 1));
if (isMap(node)) {
const result = Object.create(null);
const keys = [];
for (const pair of node.items) {
if (!isScalar(pair.key) || typeof pair.key.value !== 'string')
fail('Chaves de objetos devem ser strings; chaves numéricas, booleanas ou complexas não são preservadas em JSON.', pair.key);
const key = pair.key.value;
if (format === 'YAML' && key === '<<' && pair.key.type === 'PLAIN')
fail('Chaves de merge YAML (<<) não são aceitas.', pair.key);
if (Object.hasOwn(result, key))
fail(`Chave duplicada: ${key}.`, pair.key);
keys.push(key);
result[key] = convert(pair.value, depth + 1);
}
onObjectKeys?.(result, keys);
return result;
}
fail('Estrutura não representável em JSON.', node);
}
if (!doc.contents)
throw new Error('Informe um documento com um valor; use null para um valor nulo.');
return convert(doc.contents);
}
function yamlToJson(input) {
return JSON.stringify(parseJsonCompatible(input, 'YAML'), null, 2);
}
return yamlToJson(input);
}
import json
import math
import re
from ruamel.yaml import YAML
from ruamel.yaml.nodes import ScalarNode, SequenceNode, MappingNode
from ruamel.yaml.tokens import AliasToken, DirectiveToken
def executar(input='nome: João 😊\ncodigo: "00123"\nativo: true\nvalor: null'):
if not input.strip() or len(input.encode("utf-16-le", errors="surrogatepass")) // 2 > 1000000: raise ValueError("Informe YAML com até 1 milhão de caracteres.")
yaml = YAML(typ="safe")
yaml.version = (1, 2)
yaml.allow_duplicate_keys = False
# Restrict ruamel's implicit resolvers to the shared YAML 1.2 Core schema.
rules = yaml.resolver.versioned_resolver
for character, entries in list(rules.items()):
filtered = []
for tag, pattern in entries:
kind = tag.removeprefix("tag:yaml.org,2002:")
if kind in ("timestamp","merge"): continue
if kind == "int": pattern = re.compile(r"^(?:[+-]?[0-9]+|0o[0-7]+|0x[0-9a-fA-F]+)$")
if kind == "float": pattern = re.compile(r"^(?:[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?|[+-]?\.(?:inf|Inf|INF)|\.(?:nan|NaN|NAN))$")
filtered.append((tag,pattern))
rules[character] = filtered
def fail(message, node=None):
mark = node.start_mark if node is not None else None
raise ValueError(message + (f" (linha {mark.line + 1}, coluna {mark.column + 1})" if mark else ""))
for token in yaml.scan(input):
if isinstance(token, AliasToken): fail("Aliases não são representáveis em JSON.")
if isinstance(token, DirectiveToken) and (token.name != "YAML" or token.value != (1,2)): fail("Somente YAML 1.2, sem diretivas customizadas.")
documents = list(yaml.compose_all(input))
if len(documents) != 1 or documents[0] is None: fail("Informe exatamente um documento com valor.")
count = 0
def convert(node, depth=0):
nonlocal count
count += 1
if count > 100000 or depth > 100: fail("Limite de 100 níveis ou 100 mil nós.", node)
kind = node.tag.removeprefix("tag:yaml.org,2002:")
if kind not in ("str","bool","null","int","float","map","seq"): fail("Tag não permitida: " + node.tag, node)
if isinstance(node, ScalarNode):
if kind == "str": return node.value
if kind == "null": return None
if kind == "bool": return node.value.lower() == "true"
raw = node.value
if kind == "int":
if "_" in raw: fail("Sintaxe numérica não aceita pelo schema Core compartilhado.",node)
signed = raw.lstrip("+-")
value = int(signed, 16 if signed.startswith("0x") else 8 if signed.startswith("0o") else 10)
if raw.startswith("-"): value = -value
else: value = yaml.constructor.construct_object(node)
if not math.isfinite(value) or (value == 0 and (raw.startswith("-") or any(c in "123456789" for c in raw.lower().split("e")[0]))) or (value % 1 == 0 and abs(value) > 9007199254740991): fail("Número não preservável; use uma string.", node)
return value
if isinstance(node, SequenceNode): return [convert(item,depth+1) for item in node.value]
if isinstance(node, MappingNode):
result = {}
for key, item in node.value:
if not isinstance(key, ScalarNode) or key.tag != "tag:yaml.org,2002:str": fail("Chaves devem ser strings.",key)
if key.value == "<<" and key.style is None: fail("Merge YAML não é permitido.",key)
if key.value in result: fail("Chave duplicada: " + key.value,key)
result[key.value] = convert(item,depth+1)
return result
fail("Estrutura não representável em JSON.",node)
return json.dumps(convert(documents[0]),ensure_ascii=False,indent=2,allow_nan=False)
<?php
use Symfony\Component\Yaml\Yaml;
require_once 'vendor/autoload.php';
function executar($input = "nome: João 😊\ncodigo: \"00123\"\nativo: true\nvalor: null") {
if (trim($input) === '' || !mb_check_encoding($input,'UTF-8') || strlen(mb_convert_encoding($input,'UTF-16LE','UTF-8'))/2 > 1000000) throw new InvalidArgumentException('Informe YAML UTF-8 com até 1 milhão de caracteres.');
// Lexical preflight protects the Core types before Symfony's scalar resolver.
$prepared = ''; $flow = 0; $position = 0; $line = 1; $blockIndent = null;
$number = function ($raw) {
$numeric = preg_match('/^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/D',$raw);
if (!$numeric) return null;
$value = (float)$raw;
if (!is_finite($value) || ($value == 0 && (str_starts_with($raw,'-') || preg_match('/[1-9]/',explode('e',strtolower($raw))[0]))) || (floor($value) === $value && abs($value)>9007199254740991)) throw new InvalidArgumentException('Número não preservável; use uma string.');
return $raw;
};
while ($position < strlen($input)) {
$c = $input[$position];
if ($c === "\n") { $prepared .= $c; $position++; $line++; continue; }
if ($blockIndent !== null) {
$end = strpos($input,"\n",$position); if ($end === false) $end = strlen($input);
$raw = substr($input,$position,$end-$position);
$indent = strlen($raw)-strlen(ltrim($raw,' '));
if (trim($raw)==='' || $indent>$blockIndent) { $prepared.=$raw; $position=$end; continue; }
$blockIndent=null;
}
if ($c === '#' || $c === '%') {
$end = strpos($input,"\n",$position); if ($end === false) $end = strlen($input);
$raw=substr($input,$position,$end-$position);
if ($c==='%' && trim($raw)!=='%YAML 1.2') throw new InvalidArgumentException('Somente YAML 1.2, sem diretivas customizadas.');
if ($c==='#') $prepared.=$raw;
$position=$end; continue;
}
if ($c === '"' || $c === "'") {
$quote=$c; $start=$position++;
while ($position<strlen($input)) {
$current=$input[$position++];
if ($quote==='"' && $current==='\\') { $position++; continue; }
if ($current===$quote) {
if ($quote==="'" && ($input[$position] ?? '')==="'") { $position++; continue; }
break;
}
}
$prepared.=substr($input,$start,$position-$start); continue;
}
if ($c==='!' || $c==='&' || $c==='*') throw new InvalidArgumentException('Tags explícitas, anchors e aliases não são suportados pelo exemplo PHP.');
if ($c==='|' || $c==='>') {
$start=strrpos(substr($input,0,$position),"\n"); $start=$start===false ? 0 : $start+1;
$prefix=substr($input,$start,$position-$start); $blockIndent=strlen($prefix)-strlen(ltrim($prefix,' '));
$end=strpos($input,"\n",$position); if ($end===false) $end=strlen($input);
$prepared.=substr($input,$position,$end-$position); $position=$end; continue;
}
if ($c==='[' || $c==='{') { $flow++; $prepared.=$c; $position++; continue; }
if ($c===']' || $c==='}') { $flow--; $prepared.=$c; $position++; continue; }
if (str_contains(" \t\r,:",$c) || ($c==='-' && str_contains(" \r\n", $input[$position+1] ?? "\n"))) { $prepared.=$c; $position++; continue; }
$start=$position;
while ($position<strlen($input)) {
$current=$input[$position]; $next=$input[$position+1] ?? '';
if ($current==="\n" || ($flow && str_contains(',[]{}',$current)) || ($current===':' && ($next==='' || str_contains(" \t\r\n,[]{}",$next))) || ($current==='#' && ($position===$start || ctype_space($input[$position-1])))) break;
$position++;
}
if ($position===$start) { $prepared.=$c; $position++; continue; }
$raw=rtrim(substr($input,$start,$position-$start)); $suffix=substr($input,$start+strlen($raw),$position-$start-strlen($raw));
$key=($input[$position] ?? '')===':';
if ($raw==='---' || $raw==='...') { $prepared.=$raw.$suffix; continue; }
if ($raw==='<<' && $key) throw new InvalidArgumentException('Merge YAML não é permitido.');
$numeric=$number($raw);
$isTyped=$numeric!==null || in_array(strtolower($raw),['true','false','null','~','.nan','.inf','-.inf','+.inf'],true) || preg_match('/^0[xo][0-9a-f]+$/iD',$raw);
if ($key && $isTyped) throw new InvalidArgumentException('Chaves devem ser strings.');
if (in_array(strtolower($raw),['.nan','.inf','-.inf','+.inf'],true)) throw new InvalidArgumentException('JSON não representa NaN ou infinito.');
if ($numeric!==null) {
// JSON decimal spelling prevents octal interpretation of leading zeros.
$value=(float)$raw;
$prepared.=json_encode(floor($value)===$value ? (int)$value : $value,JSON_THROW_ON_ERROR).$suffix;
} elseif (preg_match('/^0[xo]([0-9a-f]+)$/iD',$raw,$m)) {
$base=$raw[1]==='x' ? 16 : 8; $value=base_convert($m[1],$base,10);
if ((float)$value>9007199254740991) throw new InvalidArgumentException('Inteiro inseguro; use string.');
$prepared.=$value.$suffix;
} elseif ($raw==='' || in_array(strtolower($raw),['true','false','null','~'],true)) $prepared.=$raw.$suffix;
else $prepared.=json_encode($raw,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR).$suffix;
}
$value=Yaml::parse($prepared,Yaml::PARSE_OBJECT_FOR_MAP | Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
if ($value===null && !preg_match('/(?:^|\n)\s*(?:---\s*)?(?:null|~)\s*(?:#.*)?(?:\n|$)/i',$input)) throw new InvalidArgumentException('Informe um documento com valor; use null.');
$count=0;
$walk=function ($node,$depth=0) use (&$walk,&$count) {
if (++$count>100000 || $depth>100) throw new InvalidArgumentException('Limite de 100 níveis ou 100 mil nós.');
if (is_object($node) && !$node instanceof stdClass) throw new InvalidArgumentException('Tipo YAML não representável em JSON.');
if (is_object($node) || is_array($node)) foreach ($node as $child) $walk($child,$depth+1);
};
$walk($value);
return preg_replace_callback('/^( +)/m',fn($match)=>str_repeat(' ',strlen($match[1])/2),json_encode($value,JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR));
}