# Fedora Spanish translation of abrt.master # This file is distributed under the same license as the abrt.master package. # # Gladys Guerrero Lozano , 2009. # Fernando Gonzalez , 2009. # Héctor Daniel Cabrera # msgid "" msgstr "" "Project-Id-Version: abrt.master.es\n" "Report-Msgid-Bugs-To: jmoskovc@redhat.com\n" "POT-Creation-Date: 2010-01-27 13:02+0000\n" "PO-Revision-Date: 2010-01-27 10:42-0300\n" "Last-Translator: Claudio Rodrigo Pereyra Diaz \n" "Language-Team: Fedora Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Spanish\n" "X-Generator: KBabel 1.11.4\n" "X-Poedit-Country: ARGENTINA\n" #: ../src/Gui/ABRTExceptions.py:6 msgid "Another client is already running, trying to wake it." msgstr "Otro cliente ya está siendo ejecutado, intentando despertarlo." #: ../src/Gui/ABRTExceptions.py:13 msgid "Got unexpected data from daemon (is the database properly updated?)." msgstr "Se obtienen datos inesperados desde el demonio (¿está la Base de Datos correctamente actualizada?)" #: ../src/Gui/ABRTPlugin.py:56 msgid "Not loaded plugins" msgstr "Complementos no cargados" #: ../src/Gui/ABRTPlugin.py:57 msgid "Analyzer plugins" msgstr "Complementos analizadores" #: ../src/Gui/ABRTPlugin.py:58 msgid "Action plugins" msgstr "Complementos de acción" #: ../src/Gui/ABRTPlugin.py:59 msgid "Reporter plugins" msgstr "Complementos de informes" #: ../src/Gui/ABRTPlugin.py:60 msgid "Database plugins" msgstr "Complementos de Bases de Datos" #: ../src/Gui/CCDBusBackend.py:74 #: ../src/Gui/CCDBusBackend.py:97 msgid "Can't connect to system dbus" msgstr "No es posible conectarse con system dbus" #: ../src/Gui/CCDBusBackend.py:104 #: ../src/Gui/CCDBusBackend.py:107 msgid "Please check if abrt daemon is running" msgstr "Por favor compruebe si el demonio ABRT se está ejecutando." #: ../src/Gui/CCDBusBackend.py:159 msgid "" "Daemon didn't return valid report info\n" "Debuginfo is missing?" msgstr "" "El demonio no devuelve un informe válido\n" "¿Falta la información de la depuración (debuginfo)?" #: ../src/Gui/ccgui.glade.h:1 msgid "(C) 2009 Red Hat, Inc." msgstr "(C) 2009 Red Hat, Inc." #: ../src/Gui/ccgui.glade.h:2 msgid "About ABRT" msgstr "Acerca de ABRT" #: ../src/Gui/ccgui.glade.h:3 #: ../src/Gui/CCMainWindow.py:9 #: ../src/Gui/report.glade.h:15 #: ../src/Gui/abrt.desktop.in.h:1 msgid "Automatic Bug Reporting Tool" msgstr "Herramienta de Informe de Errores Automática" #: ../src/Gui/ccgui.glade.h:4 msgid "Delete" msgstr "Eliminar" #: ../src/Gui/ccgui.glade.h:5 msgid "Details" msgstr "Detalles" #: ../src/Gui/ccgui.glade.h:6 msgid "Not Reported" msgstr "No informado" #: ../src/Gui/ccgui.glade.h:7 msgid "Please wait.." msgstr "Por favor aguarde..." #: ../src/Gui/ccgui.glade.h:8 msgid "Plugins" msgstr "Complementos" #: ../src/Gui/ccgui.glade.h:9 msgid "Report" msgstr "Informe" #: ../src/Gui/ccgui.glade.h:10 msgid "" "This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with this program. If not, see ." msgstr "" "Este programa es software libre; puede redistribuirlo y/o modificarlo bajo los términos de la Licencia Pública General de Bibliotecas de GNU, como fueron publicadas por la Fundación de Software Libre; ya sea la versión 2 o (a su elección) alguna posterior.\n" "\n" "\n" "Este programa se distribuye con la esperanza de que será útil, pero SIN NINGUNA GARANTÍA; sin siquiera la garantía implícita de MERCANTIBILIDAD o AJUSTE A UN PROPÓSITO PARTICULAR. Vea la Licencia Pública General de Bibliotecas de GNU para más detalles.\n" "\n" "Debe haber recibido una copia de la Licencia Pública General de Bibliotecas de GNU junto con este programa; sino, see ." #: ../src/Gui/ccgui.glade.h:15 msgid "_Edit" msgstr "_Editar" #: ../src/Gui/ccgui.glade.h:16 msgid "_File" msgstr "_Archivo" #: ../src/Gui/ccgui.glade.h:17 msgid "_Help" msgstr "Ay_uda" # Revisar si hay que traducir. # Ver como referencia los botones gtk que no habia que traducirlos #. add pixbuff separatelly #: ../src/Gui/CCMainWindow.py:79 msgid "Icon" msgstr "Icono" #: ../src/Gui/CCMainWindow.py:87 msgid "Package" msgstr "Paquete" #: ../src/Gui/CCMainWindow.py:88 msgid "Application" msgstr "Aplicación" #: ../src/Gui/CCMainWindow.py:89 msgid "Date" msgstr "Fecha" #: ../src/Gui/CCMainWindow.py:90 msgid "Crash count" msgstr "Cantidad de caídas" #: ../src/Gui/CCMainWindow.py:91 msgid "User" msgstr "Usuario" #: ../src/Gui/CCMainWindow.py:159 #, python-format msgid "" "Can't show the settings dialog\n" "%s" msgstr "" "No se puede mostrar el diálogo de configuración\n" "%s" #: ../src/Gui/CCMainWindow.py:170 #, python-format msgid "" "Unable to finish current task!\n" "%s" msgstr "" "¡No se pudo finalizar la tarea actual!\n" "%s" #. there is something wrong with the daemon if we cant get the dumplist #: ../src/Gui/CCMainWindow.py:197 #, python-format msgid "" "Error while loading the dumplist.\n" "%s" msgstr "" "Error intentando cargar la lista de volcado.\n" "%s" #: ../src/Gui/CCMainWindow.py:235 msgid "This crash has been reported:\n" msgstr "Esta caída ha sido reportada:\n" #: ../src/Gui/CCMainWindow.py:236 msgid "This crash has been reported:\n" msgstr " Esta caída ha sido reportada:\n" #: ../src/Gui/CCMainWindow.py:255 msgid "Not reported!" msgstr "¡No informado!" #: ../src/Gui/CCMainWindow.py:303 msgid "" "Unable to get report!\n" "Debuginfo is missing?" msgstr "" "Incapaz de conseguir el informe!\n" "¿Falta la información de la depuración?" #: ../src/Gui/CCMainWindow.py:323 #, python-format msgid "" "Reporting failed!\n" "%s" msgstr "" "¡El informe falló!\n" "%s" #: ../src/Gui/CCMainWindow.py:342 #: ../src/Gui/CCMainWindow.py:369 #, python-format msgid "Error getting the report: %s" msgstr "Error al obtener el informe: %s" #. default texts #: ../src/Gui/CCReporterDialog.py:24 msgid "Brief description how to reproduce this or what you did..." msgstr "Breve descripción de cómo reproducir esto o lo que hizo..." #: ../src/Gui/CCReporterDialog.py:94 msgid "You must agree with submitting the backtrace." msgstr "Usted debe estar de acuerdo con rendir informe del trazado" #: ../src/Gui/CCReporterDialog.py:105 #, python-format msgid "" "Reporting disabled because the backtrace is unusable.\n" "Please try to install debuginfo manually using command: debuginfo-install %s \n" "then use Refresh button to regenerate the backtrace." msgstr "" "El informe está deshabilitado porque el trazado no es útil.\n" "Por favor, intente instalar el paquete debuginfo manualmente\n" "usando el comando debuginfo-install %s \n" "y luego use el botón Actualizar para regenerar el trazado." #: ../src/Gui/CCReporterDialog.py:107 msgid "The backtrace is unusable, you can't report this!" msgstr "¡El trazado es inútil, no se puede informar esto!" #: ../src/Gui/CCReporterDialog.py:110 msgid "The backtrace is incomplete, please make sure you provide good steps to reproduce." msgstr "El trazado está incompleto, por favor, asegúrese de proveer bien los pasos para reproducir el error." #: ../src/Gui/CCReporterDialog.py:152 #, python-format msgid "" "Can't save plugin settings:\n" " %s" msgstr "" "No se pudo guardar la configuración del complemento:\n" "%s" #: ../src/Gui/dialogs.glade.h:1 #: ../src/Gui/report.glade.h:17 msgid "Log" msgstr "Registro" #: ../src/Gui/dialogs.glade.h:2 msgid "Report done" msgstr "Informe terminado" #: ../src/Gui/PluginSettingsUI.py:18 msgid "Can't find PluginDialog widget in UI description!" msgstr "No se encuentra el control visual de PluginDialog en la descripción de la interfaz del usuario." #. we shouldn't get here, but just to be safe #: ../src/Gui/PluginSettingsUI.py:24 #, python-format msgid "No UI for plugin %s" msgstr "No hay interfase del usuario para el complemento %s" #: ../src/Gui/PluginSettingsUI.py:55 #: ../src/Gui/PluginSettingsUI.py:81 msgid "combo box is not implemented" msgstr "el combo box no está implementado" #: ../src/Gui/PluginSettingsUI.py:64 msgid "Nothing to hydrate!" msgstr "¡Nada para hidratar!" #: ../src/Gui/report.glade.h:1 msgid " " msgstr " " #: ../src/Gui/report.glade.h:2 msgid "Attachments" msgstr "Adjuntos" #: ../src/Gui/report.glade.h:3 msgid "Backtrace" msgstr "Trazado" #: ../src/Gui/report.glade.h:4 msgid "Comment" msgstr "Comentario" #: ../src/Gui/report.glade.h:5 msgid "How to reproduce (in a few simple steps)" msgstr "Como reproducirlo (en unos pocos y simples pasos)" #: ../src/Gui/report.glade.h:6 msgid "Please fix the following problems" msgstr "Por favor corregir los siguientes problemas" #: ../src/Gui/report.glade.h:7 msgid "Architecture:" msgstr "Arquitectura" #: ../src/Gui/report.glade.h:8 msgid "Cmdline:" msgstr "Línea de comandos" #: ../src/Gui/report.glade.h:9 msgid "Component:" msgstr "Componente" #: ../src/Gui/report.glade.h:10 msgid "Executable:" msgstr "Ejecutable" #: ../src/Gui/report.glade.h:11 msgid "Kernel:" msgstr "Núcleo" #: ../src/Gui/report.glade.h:12 msgid "Package:" msgstr "Paquete" #: ../src/Gui/report.glade.h:13 msgid "Reason:" msgstr "Razón" #: ../src/Gui/report.glade.h:14 msgid "Release:" msgstr "Release" #: ../src/Gui/report.glade.h:16 msgid "I agree to submit this backtrace, which could contain sensitive data" msgstr "Estoy de acuerdo en rendir el siguiente trazado, el cual podría contene datos sensibles" #: ../src/Gui/report.glade.h:18 msgid "N/A" msgstr "N/A" #: ../src/Gui/report.glade.h:19 msgid "Send" msgstr "Enviar" #: ../src/Gui/SettingsDialog.py:33 #: ../src/Gui/SettingsDialog.py:50 msgid "Select plugin" msgstr "Seleccione un complemento" #: ../src/Gui/SettingsDialog.py:36 msgid "Select database backend" msgstr "Seleccione la base de datos de fondo" #: ../src/Gui/SettingsDialog.py:166 msgid "Remove this job" msgstr "Eliminar este trabajo" #: ../src/Gui/SettingsDialog.py:210 msgid "Remove this action" msgstr "Eliminar esta acción" #: ../src/Gui/settings.glade.h:1 msgid "Analyzer plugin" msgstr "Complementos analizadores" #: ../src/Gui/settings.glade.h:2 msgid "Associated action" msgstr "Acción asociada" #: ../src/Gui/settings.glade.h:3 msgid "Plugin details" msgstr "Detalles del complemento" #: ../src/Gui/settings.glade.h:4 msgid "Plugin" msgstr "complemento" #: ../src/Gui/settings.glade.h:5 msgid "Time (or period)" msgstr "Tiempo (o periodo)" #: ../src/Gui/settings.glade.h:6 msgid "Analyzers, Actions, Reporters" msgstr "Analizadores, Acciones, Informadores" #: ../src/Gui/settings.glade.h:7 msgid "Author:" msgstr "Autor:" #: ../src/Gui/settings.glade.h:8 msgid "Blacklisted packages: " msgstr "Paquetes en la Blacklist" #: ../src/Gui/settings.glade.h:9 msgid "C_onfigure plugin" msgstr "C_onfigurar Complemento" #: ../src/Gui/settings.glade.h:10 msgid "Check package GPG signature" msgstr "Compruebe la firma GPG del paquete" #: ../src/Gui/settings.glade.h:11 msgid "Common" msgstr "Común" #: ../src/Gui/settings.glade.h:12 msgid "Cron" msgstr "Cron" #: ../src/Gui/settings.glade.h:13 msgid "Database backend: " msgstr "Backend de la Base de Datos" #: ../src/Gui/settings.glade.h:14 msgid "Description:" msgstr "Descripción:" #: ../src/Gui/settings.glade.h:15 msgid "GPG Keys" msgstr "LLaves GPG" #: ../src/Gui/settings.glade.h:16 msgid "GPG keys: " msgstr "LLaves GPG:" #: ../src/Gui/settings.glade.h:17 msgid "Global Settings" msgstr "Preferencias globales" #: ../src/Gui/settings.glade.h:18 msgid "Max coredump storage size(MB):" msgstr "Capacidad máxima de almacenamiento del volcado del núcleo (MB):" #: ../src/Gui/settings.glade.h:19 msgid "Name:" msgstr "Nombre:" #: ../src/Gui/settings.glade.h:20 msgid "Settings" msgstr "Preferencias" #: ../src/Gui/settings.glade.h:21 msgid "Version:" msgstr "Versión:" #: ../src/Gui/settings.glade.h:22 msgid "Web Site:" msgstr "Página Web:" # NO TRADUCIR, hacen referencia al boton gkt #: ../src/Gui/settings.glade.h:23 msgid "gtk-cancel" msgstr "gtk-cancel" # NO TRADUCIR, hacen referencia al boton gkt #: ../src/Gui/settings.glade.h:24 msgid "gtk-ok" msgstr "gtk-ok" #: ../src/Gui/abrt.desktop.in.h:2 msgid "View and report application crashes" msgstr "Ver e informar el cuelgue de una aplicación" #: ../src/Applet/Applet.cpp:78 #, c-format msgid "A crash in package %s has been detected" msgstr "Ha sido detectado una caída en el paquete %s." #: ../src/Applet/Applet.cpp:253 msgid "ABRT service is not running" msgstr "El servicio ABRT no se está ejecutando" #: ../src/Applet/CCApplet.cpp:200 msgid "Warning" msgstr "Aviso" #: ../src/Daemon/Daemon.cpp:473 msgid "Report size exceeded the quota. Please check system's MaxCrashReportsSize value in abrt.conf." msgstr "El tamaño del informe excede la cuota. Por favor, verifique el valor de MaxCrashReportsSize del sistema en abrt.conf." #: ../lib/Plugins/Bugzilla.cpp:132 #: ../lib/Plugins/Bugzilla.cpp:300 msgid "Missing member 'bugs'" msgstr "Faltante de miembro 'bugs'" #: ../lib/Plugins/Bugzilla.cpp:184 msgid "Missing member 'cc'" msgstr "Facltante de miembro 'cc'" #: ../lib/Plugins/Bugzilla.cpp:279 #, c-format msgid "Bug is already reported: %i" msgstr "El error ya ha sido informado: %i" #: ../lib/Plugins/Bugzilla.cpp:291 msgid "Missing member 'bug_id'" msgstr "Faltante de miembro 'bug_id'" #: ../lib/Plugins/Bugzilla.cpp:367 #, c-format msgid "New bug id: %i" msgstr "Nuevo id del error: %i" #: ../lib/Plugins/Bugzilla.cpp:469 msgid "Checking for duplicates..." msgstr "Chequeando si hay duplicados..." #: ../lib/Plugins/Bugzilla.cpp:475 msgid "Empty login and password. Please check Bugzilla.conf" msgstr "Usuario y contraseña vacios. Por favor compruebe el archivo Bugzilla.conf" #: ../lib/Plugins/Bugzilla.cpp:478 msgid "Logging into bugzilla..." msgstr "Ingresando a bugzilla..." #: ../lib/Plugins/Bugzilla.cpp:483 msgid "Checking CC..." msgstr "Chequeando CC..." #: ../lib/Plugins/Bugzilla.cpp:494 msgid "Creating new bug..." msgstr "Creando un nuevo informe..." #: ../lib/Plugins/Bugzilla.cpp:498 msgid "Logging out..." msgstr "Saliendo..." #: ../lib/Plugins/Kerneloops.cpp:35 msgid "Getting local universal unique identification" msgstr "Obteniendo la identificación única universal local" #: ../lib/Plugins/CCpp.cpp:253 msgid "Generating backtrace" msgstr "Generando seguimiento..." #: ../lib/Plugins/CCpp.cpp:375 msgid "Starting debuginfo installation" msgstr "Iniciando la instalación de la información de depuración" #: ../lib/Plugins/CCpp.cpp:524 msgid "Getting local universal unique identification..." msgstr "Obteniendo la identificación única universal local..." #: ../lib/Plugins/CCpp.cpp:543 msgid "Getting global universal unique identification..." msgstr "Obteniendo la identificación única universal global..." #: ../lib/Plugins/CCpp.cpp:721 msgid "Skipping debuginfo installation" msgstr "Omita la instalación de la información de depuración" #: ../lib/Plugins/KerneloopsReporter.cpp:100 msgid "Creating and submitting a report..." msgstr "Creando y enviando un informe..." #: ../lib/Plugins/Logger.cpp:82 #, c-format msgid "Writing report to '%s'" msgstr "Escribiendo reporte en '%s'" #: ../lib/Plugins/FileTransfer.cpp:54 msgid "FileTransfer: URL not specified" msgstr "Transferencia de archivo: URL no especificada" #: ../lib/Plugins/FileTransfer.cpp:58 #, c-format msgid "Sending archive %s to %s" msgstr "Enviando archivo %s a %s" #: ../lib/Plugins/FileTransfer.cpp:289 msgid "File Transfer: Creating a report..." msgstr "Transferencia de archivo: Creando un informe..." #: ../lib/Plugins/FileTransfer.cpp:323 #, c-format msgid "Can't create and send an archive: %s" msgstr "No se puede crear y enviar un archivo: %s" #: ../lib/Plugins/FileTransfer.cpp:352 #, c-format msgid "Can't create and send an archive %s" msgstr "No se puede crear y enviar un archivo %s" #: ../lib/Plugins/KerneloopsScanner.cpp:79 msgid "Creating kernel oops crash reports..." msgstr "Creando un informe de cuelgue de kernel oops..." #: ../lib/Plugins/Mailx.cpp:137 msgid "Sending an email..." msgstr "Enviando un correo..." #: ../lib/Plugins/SOSreport.cpp:102 #, c-format msgid "Running sosreport: %s" msgstr "Corriendo sosreport: %s" #: ../lib/Plugins/SOSreport.cpp:108 msgid "Done running sosreport" msgstr "Sosreport ya esta corriendo" #~ msgid "Executing SOSreport plugin..." #~ msgstr "Ejecutando complemento SOSreport..." #~ msgid "" #~ "WARNING, you're about to send data which might contain sensitive " #~ "information.\n" #~ "Do you really want to send %s?\n" #~ msgstr "" #~ "ADVERTENCIA, ¡está por enviar datos que pueden contener " #~ "información sensible!\n" #~ "¿Realmente quiere enviar %s?\n" #~ msgid "Following items will be sent" #~ msgstr "Serán enviados los siguientes elementos" #~ msgid "Plugin name is not set, can't load its settings" #~ msgstr "" #~ "No se puso el nombre del complemento, no se puede cargar su configuración" #~ msgid "Searching for debug-info packages..." #~ msgstr "Buscando paquetes de información del depurador..." #~ msgid "Downloading and installing debug-info packages..." #~ msgstr "Descargando e instalando paquetes de información del depurador..." #~ msgid "Creating a report..." #~ msgstr "Creando un informe..." #~ msgid "Starting report creation..." #~ msgstr "Iniciando la creación del informe..." #~ msgid "Working..." #~ msgstr "Trabajando..." #~ msgid "Can't get username for uid %s" #~ msgstr "No se pudo obtener nombre de usuario para el uid %s" #~ msgid "Edit blacklisted packages" #~ msgstr "Edite los paquetes de la Blacklist" #~ msgid "Nothing selected" #~ msgstr "Nada seleccionado" #~ msgid "This function is not implemented yet!" #~ msgstr "¡Esta función no está implementada todavía! :-(" d='n459' href='#n459'>459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
# == sentence library
#
# = Features
#
# * syntax based sentences generation
# * sentence operations such as substitution.
#
# = Example
#
# Some arithmetic expressions using "+", "-", "*" and "/" are generated as follows.
#
#  require 'sentence'
#  Sentence.each({
#    :exp => [["num"],
#             [:exp, "+", :exp],
#             [:exp, "-", :exp],
#             [:exp, "*", :exp],
#             [:exp, "/", :exp]]
#  }, :exp, 2) {|sent| p sent }
#  #=>
#  #<Sentence: "num">
#  #<Sentence: ("num") "+" ("num")>
#  #<Sentence: ("num") "+" (("num") "+" ("num"))>
#  #<Sentence: ("num") "+" (("num") "-" ("num"))>
#  #<Sentence: ("num") "+" (("num") "*" ("num"))>
#  #<Sentence: ("num") "+" (("num") "/" ("num"))>
#  #<Sentence: (("num") "+" ("num")) "+" ("num")>
#  ...
#
# Sentence.each takes 3 arguments.
# The first argument is the syntax for the expressions.
# The second argument, :exp, is a generating nonterminal.
# The third argument, 2, limits derivation to restrict results finitely.
#
# Some arithmetic expressions including parenthesis can be generated as follows.
#
#  syntax = {
#    :factor => [["n"],
#                ["(", :exp, ")"]],
#    :term => [[:factor],
#              [:term, "*", :factor],
#              [:term, "/", :factor]],
#    :exp => [[:term],
#             [:exp, "+", :term],
#             [:exp, "-", :term]]
#  }
#  Sentence.each(syntax, :exp, 2) {|sent| p sent }
#  #=>
#  #<Sentence: (("n"))>
#  #<Sentence: (("(" ((("n"))) ")"))>
#  #<Sentence: (("(" ((("(" ((("n"))) ")"))) ")"))>
#  #<Sentence: (("(" (((("n")) "*" ("n"))) ")"))>
#  #<Sentence: (("(" (((("n")) "/" ("n"))) ")"))>
#  #<Sentence: (("(" (((("n"))) "+" (("n"))) ")"))>
#  #<Sentence: (("(" (((("n"))) "-" (("n"))) ")"))>
#  #<Sentence: ((("n")) "*" ("n"))>
#  #<Sentence: ((("n")) "*" ("(" ((("n"))) ")"))>
#  ...
#
#  Sentence#to_s can be used to concatenate strings
#  in a sentence:
#
#  Sentence.each(syntax, :exp, 2) {|sent| p sent.to_s }
#  #=>
#  "n"
#  "(n)"
#  "((n))"
#  "(n*n)"
#  "(n/n)"
#  "(n+n)"
#  "(n-n)"
#  "n*n"
#  "n*(n)"
#  ...
#

# Sentence() instantiates a sentence object.
#
#  Sentence("foo", "bar")
#  #=> #<Sentence: "foo" "bar">
#
#  Sentence("foo", ["bar", "baz"])
#  #=> #<Sentence: "foo" ("bar" "baz")>
#
def Sentence(*ary)
  Sentence.new(ary)
end

# Sentence class represents a tree with string leaves.
#
class Sentence
  # _ary_ represents a tree.
  # It should be a possibly nested array which contains strings.
  #
  # Note that _ary_ is not copied.
  # Don't modify _ary_ after the sentence object is instantiated.
  #
  #  Sentence.new(["a", "pen"])
  #  #<Sentence: "a" "pen">
  #
  #  Sentence.new(["I", "have", ["a", "pen"]])
  #  #<Sentence: "I" "have" ("a" "pen")>
  #
  def initialize(ary)
    @sent = ary
  end

  # returns a string which is concatenation of all strings.
  # No separator is used.
  #
  #  Sentence("2", "+", "3").to_s
  #  "2+3"
  #
  #  Sentence("2", "+", ["3", "*", "5"]).to_s
  #  "2+3*5"
  #
  def to_s
    @sent.join('')
  end

  # returns a string which is concatenation of all strings separated by _sep_.
  # If _sep_ is not given, single space is used.
  #
  #  Sentence("I", "have", ["a", "pen"]).join
  #  "I have a pen"
  #
  #  Sentence("I", "have", ["a", "pen"]).join("/")
  #  "I/have/a/pen"
  #
  #  Sentence("a", [], "b").join("/")
  #  "a/b"
  #
  def join(sep=' ')
    @sent.flatten.join(sep)
  end

  # returns a tree as a nested array.
  #
  # Note that the result is not copied.
  # Don't modify the result.
  #
  #  Sentence(["foo", "bar"], "baz").to_a
  #  #=> [["foo", "bar"], "baz"]
  #
  def to_a
    @sent
  end

  # returns <i>i</i>th element as a sentence or string.
  #
  #  s = Sentence(["foo", "bar"], "baz")
  #  s    #=> #<Sentence: ("foo" "bar") "baz">
  #  s[0] #=> #<Sentence: "foo" "bar">
  #  s[1] #=> "baz"
  #
  def [](i)
    e = @sent[i]
    e.respond_to?(:to_ary) ? Sentence.new(e) : e
  end

  # returns the number of top level elements.
  #
  #  Sentence.new(%w[foo bar]).length
  #  #=> 2
  #
  #  Sentence(%w[2 * 7], "+", %w[3 * 5]).length
  #  #=> 3
  #
  def length
    @sent.length
  end

  # iterates over children.
  #
  #  Sentence(%w[2 * 7], "+", %w[3 * 5]).each {|v| p v }
  #  #=>
  #  #<Sentence: "2" "*" "7">
  #  "+"
  #  #<Sentence: "3" "*" "5">
  #
  def each # :yield: element
    @sent.each_index {|i|
      yield self[i]
    }
  end
  include Enumerable

  def inspect
    "#<#{self.class}: #{inner_inspect(@sent, '')}>"
  end

  # :stopdoc:
  def inner_inspect(ary, r)
    first = true
    ary.each {|obj|
      r << ' ' if !first
      first = false
      if obj.respond_to? :to_ary
        r << '('
        inner_inspect(obj, r)
        r << ')'
      else
        r << obj.inspect
      end
    }
    r
  end
  # :startdoc:

  # returns new sentence object which
  # _target_ is substituted by the block.
  #
  # Sentence#subst invokes <tt>_target_ === _string_</tt> for each
  # string in the sentence.
  # The strings which === returns true are substituted by the block.
  # The block is invoked with the substituting string.
  #
  #  Sentence.new(%w[2 + 3]).subst("+") { "*" }
  #  #<Sentence: "2" "*" "3">
  #
  #  Sentence.new(%w[2 + 3]).subst(/\A\d+\z/) {|s| ((s.to_i)*2).to_s }
  #  #=> #<Sentence: "4" "+" "6">
  #
  def subst(target, &b) # :yield: string
    Sentence.new(subst_rec(@sent, target, &b))
  end

  # :stopdoc:
  def subst_rec(obj, target, &b)
    if obj.respond_to? :to_ary
      a = []
      obj.each {|e| a << subst_rec(e, target, &b) }
      a
    elsif target === obj
      yield obj
    else
      obj
    end
  end
  # :startdoc:

  # find a subsentence and return it.
  # The block is invoked for each subsentence in preorder manner.
  # The first subsentence which the block returns true is returned.
  #
  #  Sentence(%w[2 * 7], "+", %w[3 * 5]).find_subtree {|s| s[1] == "*" }
  #  #=> #<Sentence: "2" "*" "7">
  #
  def find_subtree(&b) # :yield: sentence
    find_subtree_rec(@sent, &b)
  end

  # :stopdoc:
  def find_subtree_rec(obj, &b)
    if obj.respond_to? :to_ary
      s = Sentence.new(obj)
      if b.call s
        return s
      else
        obj.each {|e|
          r = find_subtree_rec(e, &b)
          return r if r
        }
      end
    end
    nil
  end
  # :startdoc:

  # returns a new sentence object which expands according to the condition
  # given by the block.
  #
  # The block is invoked for each subsentence.
  # The subsentences which the block returns true are
  # expanded into parent.
  #
  #  s = Sentence(%w[2 * 7], "+", %w[3 * 5])
  #  #=> #<Sentence: ("2" "*" "7") "+" ("3" "*" "5")>
  #
  #  s.expand { true }
  #  #=> #<Sentence: "2" "*" "7" "+" "3" "*" "5">
  #
  #  s.expand {|s| s[0] == "3" }
  #  #=> #<Sentence: (("2" "*" "7") "+" "3" "*" "5")>
  #
  def expand(&b) # :yield: sentence
    Sentence.new(expand_rec(@sent, &b))
  end

  # :stopdoc:
  def expand_rec(obj, r=[], &b)
    if obj.respond_to? :to_ary
      obj.each {|o|
        s = Sentence.new(o)
        if b.call s
          expand_rec(o, r, &b)
        else
          a = []
          expand_rec(o, a, &b)
          r << a
        end
      }
    else
      r << obj
    end
    r
  end
  # :startdoc:

  # Sentence.each generates sentences
  # by deriving the start symbol _sym_ using _syntax_.
  # The derivation is restricted by an positive integer _limit_ to
  # avoid infinite generation.
  #
  # Sentence.each yields the block with a generated sentence.
  #
  #  Sentence.each({
  #    :exp => [["n"],
  #             [:exp, "+", :exp],
  #             [:exp, "*", :exp]]
  #    }, :exp, 1) {|sent| p sent }
  #  #=>
  #  #<Sentence: "n">
  #  #<Sentence: ("n") "+" ("n")>
  #  #<Sentence: ("n") "*" ("n")>
  #
  #  Sentence.each({
  #    :exp => [["n"],
  #             [:exp, "+", :exp],
  #             [:exp, "*", :exp]]
  #    }, :exp, 2) {|sent| p sent }
  #  #=>
  #  #<Sentence: "n">
  #  #<Sentence: ("n") "+" ("n")>
  #  #<Sentence: ("n") "+" (("n") "+" ("n"))>
  #  #<Sentence: ("n") "+" (("n") "*" ("n"))>
  #  #<Sentence: (("n") "+" ("n")) "+" ("n")>
  #  #<Sentence: (("n") "*" ("n")) "+" ("n")>
  #  #<Sentence: ("n") "*" ("n")>
  #  #<Sentence: ("n") "*" (("n") "+" ("n"))>
  #  #<Sentence: ("n") "*" (("n") "*" ("n"))>
  #  #<Sentence: (("n") "+" ("n")) "*" ("n")>
  #  #<Sentence: (("n") "*" ("n")) "*" ("n")>
  #
  def Sentence.each(syntax, sym, limit)
    Gen.new(syntax).each_tree(sym, limit) {|tree|
      yield Sentence.new(tree)
    }
  end

  # Sentence.expand_syntax returns an expanded syntax:
  # * No rule derives to empty sequence
  # * Underivable rule simplified
  # * No channel rule
  # * Symbols which has zero or one choices are not appered in rhs.
  #
  # Note that the rules which can derive empty and non-empty
  # sequences are modified to derive only non-empty sequences.
  #
  #  Sentence.expand_syntax({
  #    :underivable1 => [],
  #    :underivable2 => [[:underivable1]],
  #    :underivable3 => [[:underivable3]],
  #    :empty_only1 => [[]],
  #    :empty_only2 => [[:just_empty1, :just_empty1]],
  #    :empty_or_not => [[], ["foo"]],
  #    :empty_or_not_2 => [[:empty_or_not, :empty_or_not]],
  #    :empty_or_not_3 => [[:empty_or_not, :empty_or_not, :empty_or_not]],
  #    :empty_or_not_4 => [[:empty_or_not_2, :empty_or_not_2]],
  #    :channel1 => [[:channeled_data]],
  #    :channeled_data => [["a", "b"], ["c", "d"]],
  #    :single_choice => [["single", "choice"]],
  #    :single_choice_2 => [[:single_choice, :single_choice]],
  #  })
  #  #=>
  #  {
  #   :underivable1=>[], # underivable rules are simplified to [].
  #   :underivable2=>[],
  #   :underivable3=>[],
  #   :empty_only1=>[], # derivation to empty sequence are removed.
  #   :empty_only2=>[],
  #   :empty_or_not=>[["foo"]], # empty sequences are removed too.
  #   :empty_or_not_2=>[["foo"], ["foo", "foo"]],
  #   :empty_or_not_3=>[["foo"], ["foo", "foo"], ["foo", "foo", "foo"]],
  #   :empty_or_not_4=> [["foo"], ["foo", "foo"], [:empty_or_not_2, :empty_or_not_2]],
  #   :channel1=>[["a", "b"], ["c", "d"]], # channel rules are removed.
  #   :channeled_data=>[["a", "b"], ["c", "d"]],
  #   :single_choice=>[["single", "choice"]], # single choice rules are expanded.
  #   :single_choice_2=>[["single", "choice", "single", "choice"]],
  #  }
  #
  #  Sentence.expand_syntax({
  #    :factor => [["n"],
  #                ["(", :exp, ")"]],
  #    :term => [[:factor],
  #              [:term, "*", :factor],
  #              [:term, "/", :factor]],
  #    :exp => [[:term],
  #             [:exp, "+", :term],
  #             [:exp, "-", :term]]
  #  })
  #  #=>
  #  {:exp=> [["n"],
  #           ["(", :exp, ")"],
  #           [:exp, "+", :term],
  #           [:exp, "-", :term],
  #           [:term, "*", :factor],
  #           [:term, "/", :factor]],
  #   :factor=> [["n"],
  #              ["(", :exp, ")"]],
  #   :term=> [["n"],
  #            ["(", :exp, ")"],
  #            [:term, "*", :factor],
  #            [:term, "/", :factor]]
  #  }
  #
  def Sentence.expand_syntax(syntax)
    Sentence::Gen.expand_syntax(syntax)
  end

  # :stopdoc:
  class Gen
    def Gen.each_tree(syntax, sym, limit, &b)
      Gen.new(syntax).each_tree(sym, limit, &b)
    end

    def Gen.each_string(syntax, sym, limit, &b)
      Gen.new(syntax).each_string(sym, limit, &b)
    end

    def initialize(syntax)
      @syntax = syntax
    end

    def self.expand_syntax(syntax)
      syntax = simplify_underivable_rules(syntax)
      syntax = simplify_emptyonly_rules(syntax)
      syntax = make_rules_no_empseq(syntax)
      syntax = expand_channel_rules(syntax)

      syntax = expand_noalt_rules(syntax)
      syntax = reorder_rules(syntax)
    end

    def self.simplify_underivable_rules(syntax)
      deribable_syms = {}
      changed = true
      while changed
        changed = false
        syntax.each {|sym, rules|
          next if deribable_syms[sym]
          rules.each {|rhs|
            if rhs.all? {|e| String === e || deribable_syms[e] }
              deribable_syms[sym] = true
              changed = true
              break
            end
          }
        }
      end
      result = {}
      syntax.each {|sym, rules|
        if deribable_syms[sym]
          rules2 = []
          rules.each {|rhs|
            rules2 << rhs if rhs.all? {|e| String === e || deribable_syms[e] }
          }
          result[sym] = rules2.uniq
        else
          result[sym] = []
        end
      }
      result
    end

    def self.simplify_emptyonly_rules(syntax)
      justempty_syms = {}
      changed = true
      while changed
        changed = false
        syntax.each {|sym, rules|
          next if justempty_syms[sym]
          if !rules.empty? && rules.all? {|rhs| rhs.all? {|e| justempty_syms[e] } }
            justempty_syms[sym] = true
            changed = true
          end
        }
      end
      result = {}
      syntax.each {|sym, rules|
        result[sym] = rules.map {|rhs| rhs.reject {|e| justempty_syms[e] } }.uniq
      }
      result
    end

    def self.expand_emptyable_syms(rhs, emptyable_syms)
      if rhs.empty?
        yield []
      else
        first = rhs[0]
        rest = rhs[1..-1]
        if emptyable_syms[first]
          expand_emptyable_syms(rest, emptyable_syms) {|rhs2|
            yield [first] + rhs2
            yield rhs2
          }
        else
          expand_emptyable_syms(rest, emptyable_syms) {|rhs2|
            yield [first] + rhs2
          }
        end
      end
    end

    def self.make_rules_no_empseq(syntax)
      emptyable_syms = {}
      changed = true
      while changed
        changed = false
        syntax.each {|sym, rules|
          next if emptyable_syms[sym]
          rules.each {|rhs|
            if rhs.all? {|e| emptyable_syms[e] }
              emptyable_syms[sym] = true
              changed = true
              break
            end
          }
        }
      end
      result = {}
      syntax.each {|sym, rules|
        rules2 = []
        rules.each {|rhs|
          expand_emptyable_syms(rhs, emptyable_syms) {|rhs2|
            next if rhs2.empty?
            rules2 << rhs2
          }
        }
        result[sym] = rules2.uniq
      }
      result
    end

    def self.expand_channel_rules(syntax)
      channel_rules = {}
      syntax.each {|sym, rules|
        channel_rules[sym] = {sym=>true}
        rules.each {|rhs|
          if rhs.length == 1 && Symbol === rhs[0]
            channel_rules[sym][rhs[0]] = true
          end
        }
      }
      changed = true
      while changed
        changed = false
        channel_rules.each {|sym, set|
          n1 = set.size
          set.keys.each {|s|
            set.update(channel_rules[s])
          }
          n2 = set.size
          changed = true if n1 < n2
        }
      end
      result = {}
      syntax.each {|sym, rules|
        rules2 = []
        channel_rules[sym].each_key {|s|
          syntax[s].each {|rhs|
            unless rhs.length == 1 && Symbol === rhs[0]
              rules2 << rhs
            end
          }
        }
        result[sym] = rules2.uniq
      }
      result
    end

    def self.expand_noalt_rules(syntax)
      noalt_syms = {}
      syntax.each {|sym, rules|
        if rules.length == 1
          noalt_syms[sym] = true
        end
      }
      result = {}
      syntax.each {|sym, rules|
        rules2 = []
        rules.each {|rhs|
          rhs2 = []
          rhs.each {|e|
            if noalt_syms[e]
              rhs2.concat syntax[e][0]
            else
              rhs2 << e
            end
          }
          rules2 << rhs2
        }
        result[sym] = rules2.uniq
      }
      result
    end

    def self.reorder_rules(syntax)
      result = {}
      syntax.each {|sym, rules|
        result[sym] = rules.sort_by {|rhs|
          [rhs.find_all {|e| Symbol === e }.length, rhs.length]
        }
      }
      result
    end

    def each_tree(sym, limit)
      generate_from_sym(sym, limit) {|_, tree|
        yield tree
      }
      nil
    end

    def each_string(sym, limit)
      generate_from_sym(sym, limit) {|_, tree|
        yield [tree].join('')
      }
      nil
    end

    def generate_from_sym(sym, limit, &b)
      return if limit < 0
      if String === sym
        yield limit, sym
      else
        rules = @syntax[sym]
        raise "undefined rule: #{sym}" if !rules
        rules.each {|rhs|
          if rhs.length == 1 || rules.length == 1
            limit1 = limit
          else
            limit1 = limit-1
          end
          generate_from_rhs(rhs, limit1, &b)
        }
      end
      nil
    end

    def generate_from_rhs(rhs, limit)
      return if limit < 0
      if rhs.empty?
        yield limit, []
      else
        generate_from_sym(rhs[0], limit) {|limit1, child|
          generate_from_rhs(rhs[1..-1], limit1) {|limit2, arr|
            yield limit2, [child, *arr]
          }
        }
      end
      nil
    end
  end
  # :startdoc:

end