From 6c5916efadbd7c834f81c6fce05a9cf9fd514dbd Mon Sep 17 00:00:00 2001 From: Sehong Na Date: Sat, 31 May 2014 12:59:14 +0900 Subject: [PATCH 1/1] Initialize Tizen 2.3 --- AUTHORS | 59 + CHANGES | 495 +++ LICENSE | 25 + MANIFEST.in | 5 + Makefile | 59 + PKG-INFO | 44 + Pygments.egg-info/PKG-INFO | 44 + Pygments.egg-info/SOURCES.txt | 270 ++ Pygments.egg-info/dependency_links.txt | 1 + Pygments.egg-info/not-zip-safe | 1 + Pygments.egg-info/top_level.txt | 1 + TODO | 22 + docs/build/api.html | 458 +++ docs/build/authors.html | 274 ++ docs/build/changelog.html | 655 ++++ docs/build/cmdline.html | 358 ++ docs/build/filterdevelopment.html | 282 ++ docs/build/filters.html | 377 ++ docs/build/formatterdevelopment.html | 374 ++ docs/build/formatters.html | 888 +++++ docs/build/index.html | 261 ++ docs/build/installation.html | 281 ++ docs/build/integrate.html | 255 ++ docs/build/lexerdevelopment.html | 691 ++++ docs/build/lexers.html | 3175 ++++++++++++++++ docs/build/moinmoin.html | 245 ++ docs/build/plugins.html | 294 ++ docs/build/quickstart.html | 390 ++ docs/build/rstdirective.html | 229 ++ docs/build/styles.html | 341 ++ docs/build/tokens.html | 541 +++ docs/build/unicode.html | 249 ++ docs/generate.py | 472 +++ docs/pygmentize.1 | 94 + docs/src/api.txt | 270 ++ docs/src/authors.txt | 5 + docs/src/changelog.txt | 5 + docs/src/cmdline.txt | 151 + docs/src/filterdevelopment.txt | 70 + docs/src/filters.txt | 42 + docs/src/formatterdevelopment.txt | 169 + docs/src/formatters.txt | 48 + docs/src/index.txt | 69 + docs/src/installation.txt | 71 + docs/src/integrate.txt | 37 + docs/src/lexerdevelopment.txt | 551 +++ docs/src/lexers.txt | 62 + docs/src/moinmoin.txt | 39 + docs/src/plugins.txt | 94 + docs/src/quickstart.txt | 202 + docs/src/rstdirective.txt | 22 + docs/src/styles.txt | 143 + docs/src/tokens.txt | 349 ++ docs/src/unicode.txt | 49 + external/markdown-processor.py | 67 + external/moin-parser.py | 112 + external/rst-directive-old.py | 77 + external/rst-directive.py | 83 + ez_setup.py | 276 ++ packaging/python-pygments.changes | 41 + packaging/python-pygments.spec | 52 + pygmentize | 7 + pygments/__init__.py | 90 + pygments/cmdline.py | 430 +++ pygments/console.py | 74 + pygments/filter.py | 74 + pygments/filters/__init__.py | 292 ++ pygments/formatter.py | 92 + pygments/formatters/__init__.py | 68 + pygments/formatters/_mapping.py | 92 + pygments/formatters/bbcode.py | 109 + pygments/formatters/html.py | 705 ++++ pygments/formatters/img.py | 517 +++ pygments/formatters/latex.py | 304 ++ pygments/formatters/other.py | 117 + pygments/formatters/rtf.py | 136 + pygments/formatters/svg.py | 154 + pygments/formatters/terminal.py | 109 + pygments/formatters/terminal256.py | 219 ++ pygments/lexer.py | 654 ++++ pygments/lexers/__init__.py | 231 ++ pygments/lexers/_clbuiltins.py | 232 ++ pygments/lexers/_luabuiltins.py | 256 ++ pygments/lexers/_mapping.py | 218 ++ pygments/lexers/_phpbuiltins.py | 3389 +++++++++++++++++ pygments/lexers/_vimbuiltins.py | 3 + pygments/lexers/agile.py | 1480 ++++++++ pygments/lexers/asm.py | 333 ++ pygments/lexers/compiled.py | 1663 ++++++++ pygments/lexers/dotnet.py | 355 ++ pygments/lexers/functional.py | 760 ++++ pygments/lexers/math.py | 419 +++ pygments/lexers/other.py | 2082 ++++++++++ pygments/lexers/parsers.py | 697 ++++ pygments/lexers/special.py | 100 + pygments/lexers/templates.py | 1296 +++++++ pygments/lexers/text.py | 1507 ++++++++ pygments/lexers/web.py | 723 ++++ pygments/plugin.py | 74 + pygments/scanner.py | 104 + pygments/style.py | 117 + pygments/styles/__init__.py | 67 + pygments/styles/autumn.py | 65 + pygments/styles/borland.py | 51 + pygments/styles/bw.py | 49 + pygments/styles/colorful.py | 81 + pygments/styles/default.py | 73 + pygments/styles/emacs.py | 72 + pygments/styles/friendly.py | 72 + pygments/styles/fruity.py | 43 + pygments/styles/manni.py | 75 + pygments/styles/murphy.py | 80 + pygments/styles/native.py | 65 + pygments/styles/pastie.py | 75 + pygments/styles/perldoc.py | 69 + pygments/styles/tango.py | 141 + pygments/styles/trac.py | 63 + pygments/styles/vim.py | 63 + pygments/styles/vs.py | 38 + pygments/token.py | 203 + pygments/unistring.py | 130 + pygments/util.py | 222 ++ scripts/check_sources.py | 242 ++ scripts/detect_missing_analyse_text.py | 30 + scripts/epydoc.css | 280 ++ scripts/find_codetags.py | 205 + scripts/find_error.py | 59 + scripts/get_vimkw.py | 38 + scripts/pylintrc | 301 ++ scripts/reindent.py | 291 ++ scripts/vim2pygments.py | 933 +++++ setup.cfg | 8 + setup.py | 74 + tests/dtds/HTML4-f.dtd | 37 + tests/dtds/HTML4-s.dtd | 869 +++++ tests/dtds/HTML4.dcl | 88 + tests/dtds/HTML4.dtd | 1092 ++++++ tests/dtds/HTML4.soc | 9 + tests/dtds/HTMLlat1.ent | 195 + tests/dtds/HTMLspec.ent | 77 + tests/dtds/HTMLsym.ent | 241 ++ tests/examplefiles/ANTLRv3.g | 608 +++ tests/examplefiles/AlternatingGroup.mu | 102 + tests/examplefiles/Constants.mo | 158 + tests/examplefiles/DancingSudoku.lhs | 411 ++ tests/examplefiles/Errors.scala | 18 + tests/examplefiles/Intro.java | 1660 ++++++++ tests/examplefiles/Makefile | 1131 ++++++ tests/examplefiles/Object.st | 4394 ++++++++++++++++++++++ tests/examplefiles/RegexMatcher.ns2 | 3518 +++++++++++++++++ tests/examplefiles/SmallCheck.hs | 378 ++ tests/examplefiles/Sudoku.lhs | 382 ++ tests/examplefiles/apache2.conf | 393 ++ tests/examplefiles/as3_test.as | 143 + tests/examplefiles/as3_test2.as | 46 + tests/examplefiles/as3_test3.as | 3 + tests/examplefiles/badcase.java | 2 + tests/examplefiles/batchfile.bat | 49 + tests/examplefiles/boot-9.scm | 1557 ++++++++ tests/examplefiles/broken/fucked_up.rb | 77 + tests/examplefiles/ceval.c | 2604 +++++++++++++ tests/examplefiles/cheetah_example.html | 13 + tests/examplefiles/classes.dylan | 20 + tests/examplefiles/condensed_ruby.rb | 10 + tests/examplefiles/database.pytb | 16 + tests/examplefiles/de.MoinMoin.po | 2461 ++++++++++++ tests/examplefiles/django_sample.html+django | 68 + tests/examplefiles/dwarf.cw | 17 + tests/examplefiles/erl_session | 10 + tests/examplefiles/escape_semicolon.clj | 1 + tests/examplefiles/evil_regex.js | 48 + tests/examplefiles/example.aspx | 27 + tests/examplefiles/example.c | 2080 ++++++++++ tests/examplefiles/example.cpp | 2363 ++++++++++++ tests/examplefiles/example.lua | 250 ++ tests/examplefiles/example.moo | 26 + tests/examplefiles/example.pas | 2708 +++++++++++++ tests/examplefiles/example.rb | 1852 +++++++++ tests/examplefiles/example.rhtml | 561 +++ tests/examplefiles/example.sh-session | 17 + tests/examplefiles/example.weechatlog | 9 + tests/examplefiles/example.xhtml | 376 ++ tests/examplefiles/example.xml | 1897 ++++++++++ tests/examplefiles/example.yaml | 302 ++ tests/examplefiles/example2.aspx | 29 + tests/examplefiles/firefox.mak | 586 +++ tests/examplefiles/format.ml | 1213 ++++++ tests/examplefiles/functional.rst | 1472 ++++++++ tests/examplefiles/genclass.clj | 510 +++ tests/examplefiles/genshi_example.xml+genshi | 193 + tests/examplefiles/genshitext_example.genshitext | 33 + tests/examplefiles/glsl.frag | 7 + tests/examplefiles/glsl.vert | 13 + tests/examplefiles/html+php_faulty.php | 1 + tests/examplefiles/irb_heredoc | 8 + tests/examplefiles/jinjadesignerdoc.rst | 713 ++++ tests/examplefiles/lighttpd_config.conf | 13 + tests/examplefiles/linecontinuation.py | 47 + tests/examplefiles/ltmain.sh | 2849 ++++++++++++++ tests/examplefiles/matlab_noreturn | 3 + tests/examplefiles/matlab_sample | 27 + tests/examplefiles/matlabsession_sample.txt | 37 + tests/examplefiles/minimal.ns2 | 4 + tests/examplefiles/moin_SyntaxReference.txt | 340 ++ tests/examplefiles/multiline_regexes.rb | 38 + tests/examplefiles/nasm_aoutso.asm | 96 + tests/examplefiles/nasm_objexe.asm | 30 + tests/examplefiles/nginx_nginx.conf | 118 + tests/examplefiles/numbers.c | 12 + tests/examplefiles/objc_example.m | 11 + tests/examplefiles/objc_example2.m | 24 + tests/examplefiles/perl_perl5db | 998 +++++ tests/examplefiles/perl_regex-delims | 120 + tests/examplefiles/perlfunc.1 | 856 +++++ tests/examplefiles/phpcomplete.vim | 567 +++ tests/examplefiles/pleac.in.rb | 1223 ++++++ tests/examplefiles/pppoe.applescript | 10 + tests/examplefiles/py3_test.txt | 2 + tests/examplefiles/pycon_test.pycon | 8 + tests/examplefiles/pytb_test2.pytb | 2 + tests/examplefiles/python25-bsd.mak | 234 ++ tests/examplefiles/qsort.prolog | 13 + tests/examplefiles/ragel-cpp_rlscan | 280 ++ tests/examplefiles/ragel-cpp_snippet | 2 + tests/examplefiles/regex.js | 22 + tests/examplefiles/ruby_func_def.rb | 11 + tests/examplefiles/sibling.prolog | 19 + tests/examplefiles/simple.md | 747 ++++ tests/examplefiles/smarty_example.html | 209 + tests/examplefiles/source.lgt | 343 ++ tests/examplefiles/sources.list | 62 + tests/examplefiles/sphere.pov | 18 + tests/examplefiles/sqlite3.sqlite3-console | 27 + tests/examplefiles/squid.conf | 27 + tests/examplefiles/string_delimiters.d | 21 + tests/examplefiles/test.R | 119 + tests/examplefiles/test.bas | 29 + tests/examplefiles/test.boo | 39 + tests/examplefiles/test.cs | 351 ++ tests/examplefiles/test.css | 49 + tests/examplefiles/test.d | 135 + tests/examplefiles/test.erl | 169 + tests/examplefiles/test.evoque | 33 + tests/examplefiles/test.html | 351 ++ tests/examplefiles/test.java | 653 ++++ tests/examplefiles/test.jsp | 24 + tests/examplefiles/test.moo | 51 + tests/examplefiles/test.myt | 166 + tests/examplefiles/test.pas | 743 ++++ tests/examplefiles/test.php | 498 +++ tests/examplefiles/test.plot | 333 ++ tests/examplefiles/test.r3 | 94 + tests/examplefiles/test.rb | 174 + tests/examplefiles/test.rhtml | 43 + tests/examplefiles/test.tcsh | 830 ++++ tests/examplefiles/test.xsl | 23 + tests/examplefiles/type.lisp | 1202 ++++++ tests/examplefiles/unicode.applescript | 5 + tests/examplefiles/while.pov | 13 + tests/examplefiles/zmlrpc.f90 | 798 ++++ tests/old_run.py | 138 + tests/run.py | 48 + tests/support.py | 15 + tests/test_basic_api.py | 230 ++ tests/test_clexer.py | 31 + tests/test_cmdline.py | 101 + tests/test_examplefiles.py | 56 + tests/test_html_formatter.py | 125 + tests/test_latex_formatter.py | 53 + tests/test_regexlexer.py | 39 + tests/test_token.py | 49 + tests/test_using_api.py | 40 + tests/test_util.py | 87 + 273 files changed, 97465 insertions(+) create mode 100644 AUTHORS create mode 100644 CHANGES create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 PKG-INFO create mode 100644 Pygments.egg-info/PKG-INFO create mode 100644 Pygments.egg-info/SOURCES.txt create mode 100644 Pygments.egg-info/dependency_links.txt create mode 100644 Pygments.egg-info/not-zip-safe create mode 100644 Pygments.egg-info/top_level.txt create mode 100644 TODO create mode 100644 docs/build/api.html create mode 100644 docs/build/authors.html create mode 100644 docs/build/changelog.html create mode 100644 docs/build/cmdline.html create mode 100644 docs/build/filterdevelopment.html create mode 100644 docs/build/filters.html create mode 100644 docs/build/formatterdevelopment.html create mode 100644 docs/build/formatters.html create mode 100644 docs/build/index.html create mode 100644 docs/build/installation.html create mode 100644 docs/build/integrate.html create mode 100644 docs/build/lexerdevelopment.html create mode 100644 docs/build/lexers.html create mode 100644 docs/build/moinmoin.html create mode 100644 docs/build/plugins.html create mode 100644 docs/build/quickstart.html create mode 100644 docs/build/rstdirective.html create mode 100644 docs/build/styles.html create mode 100644 docs/build/tokens.html create mode 100644 docs/build/unicode.html create mode 100644 docs/generate.py create mode 100644 docs/pygmentize.1 create mode 100644 docs/src/api.txt create mode 100644 docs/src/authors.txt create mode 100644 docs/src/changelog.txt create mode 100644 docs/src/cmdline.txt create mode 100644 docs/src/filterdevelopment.txt create mode 100644 docs/src/filters.txt create mode 100644 docs/src/formatterdevelopment.txt create mode 100644 docs/src/formatters.txt create mode 100644 docs/src/index.txt create mode 100644 docs/src/installation.txt create mode 100644 docs/src/integrate.txt create mode 100644 docs/src/lexerdevelopment.txt create mode 100644 docs/src/lexers.txt create mode 100644 docs/src/moinmoin.txt create mode 100644 docs/src/plugins.txt create mode 100644 docs/src/quickstart.txt create mode 100644 docs/src/rstdirective.txt create mode 100644 docs/src/styles.txt create mode 100644 docs/src/tokens.txt create mode 100644 docs/src/unicode.txt create mode 100644 external/markdown-processor.py create mode 100644 external/moin-parser.py create mode 100644 external/rst-directive-old.py create mode 100644 external/rst-directive.py create mode 100755 ez_setup.py create mode 100644 packaging/python-pygments.changes create mode 100644 packaging/python-pygments.spec create mode 100755 pygmentize create mode 100644 pygments/__init__.py create mode 100644 pygments/cmdline.py create mode 100644 pygments/console.py create mode 100644 pygments/filter.py create mode 100644 pygments/filters/__init__.py create mode 100644 pygments/formatter.py create mode 100644 pygments/formatters/__init__.py create mode 100755 pygments/formatters/_mapping.py create mode 100644 pygments/formatters/bbcode.py create mode 100644 pygments/formatters/html.py create mode 100644 pygments/formatters/img.py create mode 100644 pygments/formatters/latex.py create mode 100644 pygments/formatters/other.py create mode 100644 pygments/formatters/rtf.py create mode 100644 pygments/formatters/svg.py create mode 100644 pygments/formatters/terminal.py create mode 100644 pygments/formatters/terminal256.py create mode 100644 pygments/lexer.py create mode 100644 pygments/lexers/__init__.py create mode 100644 pygments/lexers/_clbuiltins.py create mode 100644 pygments/lexers/_luabuiltins.py create mode 100644 pygments/lexers/_mapping.py create mode 100644 pygments/lexers/_phpbuiltins.py create mode 100644 pygments/lexers/_vimbuiltins.py create mode 100644 pygments/lexers/agile.py create mode 100644 pygments/lexers/asm.py create mode 100644 pygments/lexers/compiled.py create mode 100644 pygments/lexers/dotnet.py create mode 100644 pygments/lexers/functional.py create mode 100644 pygments/lexers/math.py create mode 100644 pygments/lexers/other.py create mode 100644 pygments/lexers/parsers.py create mode 100644 pygments/lexers/special.py create mode 100644 pygments/lexers/templates.py create mode 100644 pygments/lexers/text.py create mode 100644 pygments/lexers/web.py create mode 100644 pygments/plugin.py create mode 100644 pygments/scanner.py create mode 100644 pygments/style.py create mode 100644 pygments/styles/__init__.py create mode 100644 pygments/styles/autumn.py create mode 100644 pygments/styles/borland.py create mode 100644 pygments/styles/bw.py create mode 100644 pygments/styles/colorful.py create mode 100644 pygments/styles/default.py create mode 100644 pygments/styles/emacs.py create mode 100644 pygments/styles/friendly.py create mode 100644 pygments/styles/fruity.py create mode 100644 pygments/styles/manni.py create mode 100644 pygments/styles/murphy.py create mode 100644 pygments/styles/native.py create mode 100644 pygments/styles/pastie.py create mode 100644 pygments/styles/perldoc.py create mode 100644 pygments/styles/tango.py create mode 100644 pygments/styles/trac.py create mode 100644 pygments/styles/vim.py create mode 100644 pygments/styles/vs.py create mode 100644 pygments/token.py create mode 100644 pygments/unistring.py create mode 100644 pygments/util.py create mode 100755 scripts/check_sources.py create mode 100644 scripts/detect_missing_analyse_text.py create mode 100644 scripts/epydoc.css create mode 100755 scripts/find_codetags.py create mode 100644 scripts/find_error.py create mode 100644 scripts/get_vimkw.py create mode 100644 scripts/pylintrc create mode 100755 scripts/reindent.py create mode 100644 scripts/vim2pygments.py create mode 100644 setup.cfg create mode 100755 setup.py create mode 100644 tests/dtds/HTML4-f.dtd create mode 100644 tests/dtds/HTML4-s.dtd create mode 100644 tests/dtds/HTML4.dcl create mode 100644 tests/dtds/HTML4.dtd create mode 100644 tests/dtds/HTML4.soc create mode 100644 tests/dtds/HTMLlat1.ent create mode 100644 tests/dtds/HTMLspec.ent create mode 100644 tests/dtds/HTMLsym.ent create mode 100644 tests/examplefiles/ANTLRv3.g create mode 100644 tests/examplefiles/AlternatingGroup.mu create mode 100644 tests/examplefiles/Constants.mo create mode 100644 tests/examplefiles/DancingSudoku.lhs create mode 100644 tests/examplefiles/Errors.scala create mode 100644 tests/examplefiles/Intro.java create mode 100644 tests/examplefiles/Makefile create mode 100644 tests/examplefiles/Object.st create mode 100644 tests/examplefiles/RegexMatcher.ns2 create mode 100644 tests/examplefiles/SmallCheck.hs create mode 100644 tests/examplefiles/Sudoku.lhs create mode 100644 tests/examplefiles/apache2.conf create mode 100644 tests/examplefiles/as3_test.as create mode 100644 tests/examplefiles/as3_test2.as create mode 100644 tests/examplefiles/as3_test3.as create mode 100644 tests/examplefiles/badcase.java create mode 100644 tests/examplefiles/batchfile.bat create mode 100644 tests/examplefiles/boot-9.scm create mode 100644 tests/examplefiles/broken/fucked_up.rb create mode 100644 tests/examplefiles/ceval.c create mode 100644 tests/examplefiles/cheetah_example.html create mode 100644 tests/examplefiles/classes.dylan create mode 100644 tests/examplefiles/condensed_ruby.rb create mode 100644 tests/examplefiles/database.pytb create mode 100644 tests/examplefiles/de.MoinMoin.po create mode 100644 tests/examplefiles/django_sample.html+django create mode 100644 tests/examplefiles/dwarf.cw create mode 100644 tests/examplefiles/erl_session create mode 100644 tests/examplefiles/escape_semicolon.clj create mode 100644 tests/examplefiles/evil_regex.js create mode 100644 tests/examplefiles/example.aspx create mode 100644 tests/examplefiles/example.c create mode 100644 tests/examplefiles/example.cpp create mode 100644 tests/examplefiles/example.lua create mode 100644 tests/examplefiles/example.moo create mode 100644 tests/examplefiles/example.pas create mode 100644 tests/examplefiles/example.rb create mode 100644 tests/examplefiles/example.rhtml create mode 100644 tests/examplefiles/example.sh-session create mode 100644 tests/examplefiles/example.weechatlog create mode 100644 tests/examplefiles/example.xhtml create mode 100644 tests/examplefiles/example.xml create mode 100644 tests/examplefiles/example.yaml create mode 100644 tests/examplefiles/example2.aspx create mode 100644 tests/examplefiles/firefox.mak create mode 100644 tests/examplefiles/format.ml create mode 100644 tests/examplefiles/functional.rst create mode 100644 tests/examplefiles/genclass.clj create mode 100644 tests/examplefiles/genshi_example.xml+genshi create mode 100644 tests/examplefiles/genshitext_example.genshitext create mode 100644 tests/examplefiles/glsl.frag create mode 100644 tests/examplefiles/glsl.vert create mode 100644 tests/examplefiles/html+php_faulty.php create mode 100644 tests/examplefiles/irb_heredoc create mode 100644 tests/examplefiles/jinjadesignerdoc.rst create mode 100644 tests/examplefiles/lighttpd_config.conf create mode 100644 tests/examplefiles/linecontinuation.py create mode 100644 tests/examplefiles/ltmain.sh create mode 100644 tests/examplefiles/matlab_noreturn create mode 100644 tests/examplefiles/matlab_sample create mode 100644 tests/examplefiles/matlabsession_sample.txt create mode 100644 tests/examplefiles/minimal.ns2 create mode 100644 tests/examplefiles/moin_SyntaxReference.txt create mode 100644 tests/examplefiles/multiline_regexes.rb create mode 100644 tests/examplefiles/nasm_aoutso.asm create mode 100644 tests/examplefiles/nasm_objexe.asm create mode 100644 tests/examplefiles/nginx_nginx.conf create mode 100644 tests/examplefiles/numbers.c create mode 100644 tests/examplefiles/objc_example.m create mode 100644 tests/examplefiles/objc_example2.m create mode 100644 tests/examplefiles/perl_perl5db create mode 100644 tests/examplefiles/perl_regex-delims create mode 100644 tests/examplefiles/perlfunc.1 create mode 100644 tests/examplefiles/phpcomplete.vim create mode 100644 tests/examplefiles/pleac.in.rb create mode 100644 tests/examplefiles/pppoe.applescript create mode 100644 tests/examplefiles/py3_test.txt create mode 100644 tests/examplefiles/pycon_test.pycon create mode 100644 tests/examplefiles/pytb_test2.pytb create mode 100644 tests/examplefiles/python25-bsd.mak create mode 100644 tests/examplefiles/qsort.prolog create mode 100644 tests/examplefiles/ragel-cpp_rlscan create mode 100644 tests/examplefiles/ragel-cpp_snippet create mode 100644 tests/examplefiles/regex.js create mode 100644 tests/examplefiles/ruby_func_def.rb create mode 100644 tests/examplefiles/sibling.prolog create mode 100644 tests/examplefiles/simple.md create mode 100644 tests/examplefiles/smarty_example.html create mode 100644 tests/examplefiles/source.lgt create mode 100644 tests/examplefiles/sources.list create mode 100644 tests/examplefiles/sphere.pov create mode 100644 tests/examplefiles/sqlite3.sqlite3-console create mode 100644 tests/examplefiles/squid.conf create mode 100644 tests/examplefiles/string_delimiters.d create mode 100644 tests/examplefiles/test.R create mode 100644 tests/examplefiles/test.bas create mode 100644 tests/examplefiles/test.boo create mode 100644 tests/examplefiles/test.cs create mode 100644 tests/examplefiles/test.css create mode 100644 tests/examplefiles/test.d create mode 100644 tests/examplefiles/test.erl create mode 100644 tests/examplefiles/test.evoque create mode 100644 tests/examplefiles/test.html create mode 100644 tests/examplefiles/test.java create mode 100644 tests/examplefiles/test.jsp create mode 100644 tests/examplefiles/test.moo create mode 100644 tests/examplefiles/test.myt create mode 100644 tests/examplefiles/test.pas create mode 100644 tests/examplefiles/test.php create mode 100644 tests/examplefiles/test.plot create mode 100644 tests/examplefiles/test.r3 create mode 100644 tests/examplefiles/test.rb create mode 100644 tests/examplefiles/test.rhtml create mode 100644 tests/examplefiles/test.tcsh create mode 100644 tests/examplefiles/test.xsl create mode 100644 tests/examplefiles/type.lisp create mode 100644 tests/examplefiles/unicode.applescript create mode 100644 tests/examplefiles/while.pov create mode 100644 tests/examplefiles/zmlrpc.f90 create mode 100644 tests/old_run.py create mode 100644 tests/run.py create mode 100644 tests/support.py create mode 100644 tests/test_basic_api.py create mode 100644 tests/test_clexer.py create mode 100644 tests/test_cmdline.py create mode 100644 tests/test_examplefiles.py create mode 100644 tests/test_html_formatter.py create mode 100644 tests/test_latex_formatter.py create mode 100644 tests/test_regexlexer.py create mode 100644 tests/test_token.py create mode 100644 tests/test_using_api.py create mode 100644 tests/test_util.py diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..fd4983b --- /dev/null +++ b/AUTHORS @@ -0,0 +1,59 @@ +Pygments is written and maintained by Georg Brandl . + +Major developers are Tim Hatch and Armin Ronacher +. + +Other contributors, listed alphabetically, are: + +* Kumar Appaiah -- Debian control lexer +* Ali Afshar -- image formatter +* Andreas Amann -- AppleScript lexer +* Stefan Matthias Aust -- Smalltalk lexer +* Ben Bangert -- Mako lexers +* Max Battcher -- Darcs patch lexer +* Michael Bayer -- Myghty lexers +* Jarrett Billingsley -- MiniD lexer +* Adam Blinkinsop -- Haskell, Redcode lexers +* Frits van Bommel -- assembler lexers +* Pierre Bourdon -- bugfixes +* Christopher Creutzig -- MuPAD lexer +* Pete Curry -- bugfixes +* Nick Efford -- Python 3 lexer +* Artem Egorkine -- terminal256 formatter +* Laurent Gautier -- R/S lexer +* Krzysiek Goj -- Scala lexer +* Matt Good -- Genshi, Cheetah lexers +* Matthew Harrison -- SVG formatter +* Steven Hazel -- Tcl lexer +* Varun Hiremath -- Debian control lexer +* Dennis Kaarsemaker -- sources.list lexer +* Marek Kubica -- Scheme lexer +* Jochen Kupperschmidt -- Markdown processor +* Gerd Kurzbach -- Modelica lexer +* Mark Lee -- Vala lexer +* Kirk McDonald -- D lexer +* Lukas Meuser -- BBCode formatter, Lua lexer +* Paulo Moura -- Logtalk lexer +* Ana Nelson -- Ragel, ANTLR lexers +* Jesper Noehr -- HTML formatter "anchorlinenos" +* Jonas Obrist -- BBCode lexer +* David Oliva -- Rebol lexer +* Ronny Pfannschmidt -- BBCode lexer +* Benjamin Peterson -- Test suite refactoring +* Justin Reidy -- MXML lexer +* Andre Roberge -- Tango style +* Mario Ruggier -- Evoque lexers +* Stou Sandalski -- NumPy, FORTRAN, tcsh and XSLT lexers +* Matteo Sasso -- Common Lisp lexer +* Ken Schutte -- Matlab lexers +* Tassilo Schweyer -- Io, MOOCode lexers +* Joerg Sieker -- ABAP lexer +* Kirill Simonov -- YAML lexer +* Tiberius Teng -- default style overhaul +* Jeremy Thurgood -- Erlang, Squid config lexers +* Whitney Young -- ObjectiveC lexer +* Dietmar Winkler -- Modelica lexer +* Nils Winter -- Smalltalk lexer +* Davy Wybiral -- Clojure lexer + +Many thanks for all contributions! diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..9985ab3 --- /dev/null +++ b/CHANGES @@ -0,0 +1,495 @@ +Pygments changelog +================== + +Issue numbers refer to the tracker at http://dev.pocoo.org/projects/pygments/. + +Version 1.1.1 +------------- +(bugfix release, released Sep 15, 2009) + +- Fixed the BBCode lexer (#435). + +- Added support for new Jinja2 keywords. + +- Fixed test suite failures. + +- Added Gentoo-specific suffixes to Bash lexer. + + +Version 1.1 +----------- +(codename Brillouin, released Sep 11, 2009) + +- Ported Pygments to Python 3. This needed a few changes in the way + encodings are handled; they may affect corner cases when used with + Python 2 as well. + +- Lexers added: + + * Antlr/Ragel, thanks to Ana Nelson + * (Ba)sh shell + * Erlang shell + * GLSL + * Prolog + * Evoque + * Modelica + * Rebol + * MXML + * Cython + * ABAP + * ASP.net (VB/C#) + * Vala + * Newspeak + +- Fixed the LaTeX formatter's output so that output generated for one style + can be used with the style definitions of another (#384). + +- Added "anchorlinenos" and "noclobber_cssfile" (#396) options to HTML + formatter. + +- Support multiline strings in Lua lexer. + +- Rewrite of the JavaScript lexer by Pumbaa80 to better support regular + expression literals (#403). + +- When pygmentize is asked to highlight a file for which multiple lexers + match the filename, use the analyse_text guessing engine to determine the + winner (#355). + +- Fixed minor bugs in the JavaScript lexer (#383), the Matlab lexer (#378), + the Scala lexer (#392), the INI lexer (#391), the Clojure lexer (#387) + and the AS3 lexer (#389). + +- Fixed three Perl heredoc lexing bugs (#379, #400, #422). + +- Fixed a bug in the image formatter which misdetected lines (#380). + +- Fixed bugs lexing extended Ruby strings and regexes. + +- Fixed a bug when lexing git diffs. + +- Fixed a bug lexing the empty commit in the PHP lexer (#405). + +- Fixed a bug causing Python numbers to be mishighlighted as floats (#397). + +- Fixed a bug when backslashes are used in odd locations in Python (#395). + +- Fixed various bugs in Matlab and S-Plus lexers, thanks to Winston Chang (#410, + #411, #413, #414) and fmarc (#419). + +- Fixed a bug in Haskell single-line comment detection (#426). + +- Added new-style reStructuredText directive for docutils 0.5+ (#428). + + +Version 1.0 +----------- +(codename Dreiundzwanzig, released Nov 23, 2008) + +- Don't use join(splitlines()) when converting newlines to ``\n``, + because that doesn't keep all newlines at the end when the + ``stripnl`` lexer option is False. + +- Add ``-N`` option to command-line interface to get a lexer name + for a given filename. + +- Add Tango style, written by Andre Roberge for the Crunchy project. + +- Add Python3TracebackLexer and ``python3`` option to + PythonConsoleLexer. + +- Fix a few bugs in the Haskell lexer. + +- Fix PythonTracebackLexer to be able to recognize SyntaxError and + KeyboardInterrupt (#360). + +- Provide one formatter class per image format, so that surprises like:: + + pygmentize -f gif -o foo.gif foo.py + + creating a PNG file are avoided. + +- Actually use the `font_size` option of the image formatter. + +- Fixed numpy lexer that it doesn't listen for `*.py` any longer. + +- Fixed HTML formatter so that text options can be Unicode + strings (#371). + +- Unified Diff lexer supports the "udiff" alias now. + +- Fix a few issues in Scala lexer (#367). + +- RubyConsoleLexer now supports simple prompt mode (#363). + +- JavascriptLexer is smarter about what constitutes a regex (#356). + +- Add Applescript lexer, thanks to Andreas Amann (#330). + +- Make the codetags more strict about matching words (#368). + +- NginxConfLexer is a little more accurate on mimetypes and + variables (#370). + + +Version 0.11.1 +-------------- +(released Aug 24, 2008) + +- Fix a Jython compatibility issue in pygments.unistring (#358). + + +Version 0.11 +------------ +(codename Straußenei, released Aug 23, 2008) + +Many thanks go to Tim Hatch for writing or integrating most of the bug +fixes and new features. + +- Lexers added: + + * Nasm-style assembly language, thanks to delroth + * YAML, thanks to Kirill Simonov + * ActionScript 3, thanks to Pierre Bourdon + * Cheetah/Spitfire templates, thanks to Matt Good + * Lighttpd config files + * Nginx config files + * Gnuplot plotting scripts + * Clojure + * POV-Ray scene files + * Sqlite3 interactive console sessions + * Scala source files, thanks to Krzysiek Goj + +- Lexers improved: + + * C lexer highlights standard library functions now and supports C99 + types. + * Bash lexer now correctly highlights heredocs without preceding + whitespace. + * Vim lexer now highlights hex colors properly and knows a couple + more keywords. + * Irc logs lexer now handles xchat's default time format (#340) and + correctly highlights lines ending in ``>``. + * Support more delimiters for perl regular expressions (#258). + * ObjectiveC lexer now supports 2.0 features. + +- Added "Visual Studio" style. + +- Updated markdown processor to Markdown 1.7. + +- Support roman/sans/mono style defs and use them in the LaTeX + formatter. + +- The RawTokenFormatter is no longer registered to ``*.raw`` and it's + documented that tokenization with this lexer may raise exceptions. + +- New option ``hl_lines`` to HTML formatter, to highlight certain + lines. + +- New option ``prestyles`` to HTML formatter. + +- New option *-g* to pygmentize, to allow lexer guessing based on + filetext (can be slowish, so file extensions are still checked + first). + +- ``guess_lexer()`` now makes its decision much faster due to a cache + of whether data is xml-like (a check which is used in several + versions of ``analyse_text()``. Several lexers also have more + accurate ``analyse_text()`` now. + + +Version 0.10 +------------ +(codename Malzeug, released May 06, 2008) + +- Lexers added: + + * Io + * Smalltalk + * Darcs patches + * Tcl + * Matlab + * Matlab sessions + * FORTRAN + * XSLT + * tcsh + * NumPy + * Python 3 + * S, S-plus, R statistics languages + * Logtalk + +- In the LatexFormatter, the *commandprefix* option is now by default + 'PY' instead of 'C', since the latter resulted in several collisions + with other packages. Also, the special meaning of the *arg* + argument to ``get_style_defs()`` was removed. + +- Added ImageFormatter, to format code as PNG, JPG, GIF or BMP. + (Needs the Python Imaging Library.) + +- Support doc comments in the PHP lexer. + +- Handle format specifications in the Perl lexer. + +- Fix comment handling in the Batch lexer. + +- Add more file name extensions for the C++, INI and XML lexers. + +- Fixes in the IRC and MuPad lexers. + +- Fix function and interface name highlighting in the Java lexer. + +- Fix at-rule handling in the CSS lexer. + +- Handle KeyboardInterrupts gracefully in pygmentize. + +- Added BlackWhiteStyle. + +- Bash lexer now correctly highlights math, does not require + whitespace after semicolons, and correctly highlights boolean + operators. + +- Makefile lexer is now capable of handling BSD and GNU make syntax. + + +Version 0.9 +----------- +(codename Herbstzeitlose, released Oct 14, 2007) + +- Lexers added: + + * Erlang + * ActionScript + * Literate Haskell + * Common Lisp + * Various assembly languages + * Gettext catalogs + * Squid configuration + * Debian control files + * MySQL-style SQL + * MOOCode + +- Lexers improved: + + * Greatly improved the Haskell and OCaml lexers. + * Improved the Bash lexer's handling of nested constructs. + * The C# and Java lexers exhibited abysmal performance with some + input code; this should now be fixed. + * The IRC logs lexer is now able to colorize weechat logs too. + * The Lua lexer now recognizes multi-line comments. + * Fixed bugs in the D and MiniD lexer. + +- The encoding handling of the command line mode (pygmentize) was + enhanced. You shouldn't get UnicodeErrors from it anymore if you + don't give an encoding option. + +- Added a ``-P`` option to the command line mode which can be used to + give options whose values contain commas or equals signs. + +- Added 256-color terminal formatter. + +- Added an experimental SVG formatter. + +- Added the ``lineanchors`` option to the HTML formatter, thanks to + Ian Charnas for the idea. + +- Gave the line numbers table a CSS class in the HTML formatter. + +- Added a Vim 7-like style. + + +Version 0.8.1 +------------- +(released Jun 27, 2007) + +- Fixed POD highlighting in the Ruby lexer. + +- Fixed Unicode class and namespace name highlighting in the C# lexer. + +- Fixed Unicode string prefix highlighting in the Python lexer. + +- Fixed a bug in the D and MiniD lexers. + +- Fixed the included MoinMoin parser. + + +Version 0.8 +----------- +(codename Maikäfer, released May 30, 2007) + +- Lexers added: + + * Haskell, thanks to Adam Blinkinsop + * Redcode, thanks to Adam Blinkinsop + * D, thanks to Kirk McDonald + * MuPad, thanks to Christopher Creutzig + * MiniD, thanks to Jarrett Billingsley + * Vim Script, by Tim Hatch + +- The HTML formatter now has a second line-numbers mode in which it + will just integrate the numbers in the same ``
`` tag as the
+  code.
+
+- The `CSharpLexer` now is Unicode-aware, which means that it has an
+  option that can be set so that it correctly lexes Unicode
+  identifiers allowed by the C# specs.
+
+- Added a `RaiseOnErrorTokenFilter` that raises an exception when the
+  lexer generates an error token, and a `VisibleWhitespaceFilter` that
+  converts whitespace (spaces, tabs, newlines) into visible
+  characters.
+
+- Fixed the `do_insertions()` helper function to yield correct
+  indices.
+
+- The ReST lexer now automatically highlights source code blocks in
+  ".. sourcecode:: language" and ".. code:: language" directive
+  blocks.
+
+- Improved the default style (thanks to Tiberius Teng). The old
+  default is still available as the "emacs" style (which was an alias
+  before).
+
+- The `get_style_defs` method of HTML formatters now uses the
+  `cssclass` option as the default selector if it was given.
+
+- Improved the ReST and Bash lexers a bit.
+
+- Fixed a few bugs in the Makefile and Bash lexers, thanks to Tim
+  Hatch.
+
+- Fixed a bug in the command line code that disallowed ``-O`` options
+  when using the ``-S`` option.
+
+- Fixed a bug in the `RawTokenFormatter`.
+
+
+Version 0.7.1
+-------------
+(released Feb 15, 2007)
+
+- Fixed little highlighting bugs in the Python, Java, Scheme and
+  Apache Config lexers.
+
+- Updated the included manpage.
+
+- Included a built version of the documentation in the source tarball.
+
+
+Version 0.7
+-----------
+(codename Faschingskrapfn, released Feb 14, 2007)
+
+- Added a MoinMoin parser that uses Pygments. With it, you get
+  Pygments highlighting in Moin Wiki pages.
+
+- Changed the exception raised if no suitable lexer, formatter etc. is
+  found in one of the `get_*_by_*` functions to a custom exception,
+  `pygments.util.ClassNotFound`. It is, however, a subclass of
+  `ValueError` in order to retain backwards compatibility.
+
+- Added a `-H` command line option which can be used to get the
+  docstring of a lexer, formatter or filter.
+
+- Made the handling of lexers and formatters more consistent. The
+  aliases and filename patterns of formatters are now attributes on
+  them.
+
+- Added an OCaml lexer, thanks to Adam Blinkinsop.
+
+- Made the HTML formatter more flexible, and easily subclassable in
+  order to make it easy to implement custom wrappers, e.g. alternate
+  line number markup. See the documentation.
+
+- Added an `outencoding` option to all formatters, making it possible
+  to override the `encoding` (which is used by lexers and formatters)
+  when using the command line interface. Also, if using the terminal
+  formatter and the output file is a terminal and has an encoding
+  attribute, use it if no encoding is given.
+
+- Made it possible to just drop style modules into the `styles`
+  subpackage of the Pygments installation.
+
+- Added a "state" keyword argument to the `using` helper.
+
+- Added a `commandprefix` option to the `LatexFormatter` which allows
+  to control how the command names are constructed.
+
+- Added quite a few new lexers, thanks to Tim Hatch:
+
+  * Java Server Pages
+  * Windows batch files
+  * Trac Wiki markup
+  * Python tracebacks
+  * ReStructuredText
+  * Dylan
+  * and the Befunge esoteric programming language (yay!)
+
+- Added Mako lexers by Ben Bangert.
+
+- Added "fruity" style, another dark background originally vim-based
+  theme.
+
+- Added sources.list lexer by Dennis Kaarsemaker.
+
+- Added token stream filters, and a pygmentize option to use them.
+
+- Changed behavior of `in` Operator for tokens. 
+
+- Added mimetypes for all lexers.
+
+- Fixed some problems lexing Python strings.
+
+- Fixed tickets: #167, #178, #179, #180, #185, #201.
+
+
+Version 0.6
+-----------
+(codename Zimtstern, released Dec 20, 2006)
+
+- Added option for the HTML formatter to write the CSS to an external
+  file in "full document" mode.
+
+- Added RTF formatter.
+
+- Added Bash and Apache configuration lexers (thanks to Tim Hatch).
+
+- Improved guessing methods for various lexers.
+
+- Added `@media` support to CSS lexer (thanks to Tim Hatch).
+
+- Added a Groff lexer (thanks to Tim Hatch).
+
+- License change to BSD.
+
+- Added lexers for the Myghty template language.
+
+- Added a Scheme lexer (thanks to Marek Kubica).
+
+- Added some functions to iterate over existing lexers, formatters and
+  lexers.
+
+- The HtmlFormatter's `get_style_defs()` can now take a list as an
+  argument to generate CSS with multiple prefixes.
+
+- Support for guessing input encoding added.
+
+- Encoding support added: all processing is now done with Unicode
+  strings, input and output are converted from and optionally to byte
+  strings (see the ``encoding`` option of lexers and formatters).
+
+- Some improvements in the C(++) lexers handling comments and line
+  continuations.
+
+
+Version 0.5.1
+-------------
+(released Oct 30, 2006)
+
+- Fixed traceback in ``pygmentize -L`` (thanks to Piotr Ozarowski).
+
+
+Version 0.5
+-----------
+(codename PyKleur, released Oct 30, 2006)
+
+- Initial public release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..05652fc
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2006-2009 by the respective authors (see AUTHORS file).
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..2a39ef4
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,5 @@
+include external/*.py
+include Makefile CHANGES LICENSE AUTHORS TODO ez_setup.py
+recursive-include tests *
+recursive-include docs *
+recursive-include scripts *
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3d27e39
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,59 @@
+#
+# Makefile for Pygments
+# ~~~~~~~~~~~~~~~~~~~~~
+#
+# Combines scripts for common tasks.
+#
+# :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS.
+# :license: BSD, see LICENSE for details.
+#
+
+PYTHON ?= python
+
+export PYTHONPATH = $(shell echo "$$PYTHONPATH"):$(shell python -c 'import os; print ":".join(os.path.abspath(line.strip()) for line in file("PYTHONPATH"))' 2>/dev/null)
+
+.PHONY: all check clean clean-pyc codetags docs mapfiles \
+	pylint reindent test test-coverage
+
+all: clean-pyc check test
+
+check:
+	@$(PYTHON) scripts/detect_missing_analyse_text.py || true
+	@$(PYTHON) scripts/check_sources.py -i pygments/lexers/_mapping.py \
+		   -i docs/build -i pygments/formatters/_mapping.py -i pygments/unistring.py \
+		   -i pygments/lexers/_vimbuiltins.py
+
+clean: clean-pyc
+	rm -r build
+	rm -f codetags.html
+
+clean-pyc:
+	find . -name '*.pyc' -exec rm -f {} +
+	find . -name '*.pyo' -exec rm -f {} +
+	find . -name '*~' -exec rm -f {} +
+
+codetags:
+	@$(PYTHON) scripts/find_codetags.py -i tests/examplefiles -i scripts/pylintrc \
+		   -i scripts/find_codetags.py -o codetags.html .
+
+docs: docs/build
+
+docs/build: docs/src/*.txt
+	$(PYTHON) docs/generate.py html docs/build $?
+	touch docs/build
+
+mapfiles:
+	(cd pygments/lexers; $(PYTHON) _mapping.py)
+	(cd pygments/formatters; $(PYTHON) _mapping.py)
+
+pylint:
+	@pylint --rcfile scripts/pylintrc pygments
+
+reindent:
+	@$(PYTHON) scripts/reindent.py -r -B .
+
+test:
+	@$(PYTHON) tests/run.py $(TESTS)
+
+test-coverage:
+	@$(PYTHON) tests/run.py -C $(TESTS)
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..d786c48
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,44 @@
+Metadata-Version: 1.0
+Name: Pygments
+Version: 1.1.1
+Summary: Pygments is a syntax highlighting package written in Python.
+Home-page: http://pygments.org/
+Author: Georg Brandl
+Author-email: georg@python.org
+License: BSD License
+Description: 
+        Pygments
+        ~~~~~~~~
+        
+        Pygments is a syntax highlighting package written in Python.
+        
+        It is a generic syntax highlighter for general use in all kinds of software
+        such as forum systems, wikis or other applications that need to prettify
+        source code. Highlights are:
+        
+        * a wide range of common languages and markup formats is supported
+        * special attention is paid to details, increasing quality by a fair amount
+        * support for new languages and formats are added easily
+        * a number of output formats, presently HTML, LaTeX, RTF, SVG and ANSI sequences
+        * it is usable as a command-line tool and as a library
+        * ... and it highlights even Brainfuck!
+        
+        The `Pygments tip`_ is installable with ``easy_install Pygments==dev``.
+        
+        .. _Pygments tip:
+        http://dev.pocoo.org/hg/pygments-main/archive/tip.tar.gz#egg=Pygments-dev
+        
+        :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS.
+        :license: BSD, see LICENSE for details.
+        
+Keywords: syntax highlighting
+Platform: any
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: End Users/Desktop
+Classifier: Intended Audience :: System Administrators
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Operating System :: OS Independent
diff --git a/Pygments.egg-info/PKG-INFO b/Pygments.egg-info/PKG-INFO
new file mode 100644
index 0000000..d786c48
--- /dev/null
+++ b/Pygments.egg-info/PKG-INFO
@@ -0,0 +1,44 @@
+Metadata-Version: 1.0
+Name: Pygments
+Version: 1.1.1
+Summary: Pygments is a syntax highlighting package written in Python.
+Home-page: http://pygments.org/
+Author: Georg Brandl
+Author-email: georg@python.org
+License: BSD License
+Description: 
+        Pygments
+        ~~~~~~~~
+        
+        Pygments is a syntax highlighting package written in Python.
+        
+        It is a generic syntax highlighter for general use in all kinds of software
+        such as forum systems, wikis or other applications that need to prettify
+        source code. Highlights are:
+        
+        * a wide range of common languages and markup formats is supported
+        * special attention is paid to details, increasing quality by a fair amount
+        * support for new languages and formats are added easily
+        * a number of output formats, presently HTML, LaTeX, RTF, SVG and ANSI sequences
+        * it is usable as a command-line tool and as a library
+        * ... and it highlights even Brainfuck!
+        
+        The `Pygments tip`_ is installable with ``easy_install Pygments==dev``.
+        
+        .. _Pygments tip:
+        http://dev.pocoo.org/hg/pygments-main/archive/tip.tar.gz#egg=Pygments-dev
+        
+        :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS.
+        :license: BSD, see LICENSE for details.
+        
+Keywords: syntax highlighting
+Platform: any
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: End Users/Desktop
+Classifier: Intended Audience :: System Administrators
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Operating System :: OS Independent
diff --git a/Pygments.egg-info/SOURCES.txt b/Pygments.egg-info/SOURCES.txt
new file mode 100644
index 0000000..d14d7b5
--- /dev/null
+++ b/Pygments.egg-info/SOURCES.txt
@@ -0,0 +1,270 @@
+AUTHORS
+CHANGES
+LICENSE
+MANIFEST.in
+Makefile
+TODO
+ez_setup.py
+pygmentize
+setup.cfg
+setup.py
+Pygments.egg-info/PKG-INFO
+Pygments.egg-info/SOURCES.txt
+Pygments.egg-info/dependency_links.txt
+Pygments.egg-info/not-zip-safe
+Pygments.egg-info/top_level.txt
+docs/generate.py
+docs/pygmentize.1
+docs/build/api.html
+docs/build/authors.html
+docs/build/changelog.html
+docs/build/cmdline.html
+docs/build/filterdevelopment.html
+docs/build/filters.html
+docs/build/formatterdevelopment.html
+docs/build/formatters.html
+docs/build/index.html
+docs/build/installation.html
+docs/build/integrate.html
+docs/build/lexerdevelopment.html
+docs/build/lexers.html
+docs/build/moinmoin.html
+docs/build/plugins.html
+docs/build/quickstart.html
+docs/build/rstdirective.html
+docs/build/styles.html
+docs/build/tokens.html
+docs/build/unicode.html
+docs/src/api.txt
+docs/src/authors.txt
+docs/src/changelog.txt
+docs/src/cmdline.txt
+docs/src/filterdevelopment.txt
+docs/src/filters.txt
+docs/src/formatterdevelopment.txt
+docs/src/formatters.txt
+docs/src/index.txt
+docs/src/installation.txt
+docs/src/integrate.txt
+docs/src/lexerdevelopment.txt
+docs/src/lexers.txt
+docs/src/moinmoin.txt
+docs/src/plugins.txt
+docs/src/quickstart.txt
+docs/src/rstdirective.txt
+docs/src/styles.txt
+docs/src/tokens.txt
+docs/src/unicode.txt
+external/markdown-processor.py
+external/moin-parser.py
+external/rst-directive-old.py
+external/rst-directive.py
+pygments/__init__.py
+pygments/cmdline.py
+pygments/console.py
+pygments/filter.py
+pygments/formatter.py
+pygments/lexer.py
+pygments/plugin.py
+pygments/scanner.py
+pygments/style.py
+pygments/token.py
+pygments/unistring.py
+pygments/util.py
+pygments/filters/__init__.py
+pygments/formatters/__init__.py
+pygments/formatters/_mapping.py
+pygments/formatters/bbcode.py
+pygments/formatters/html.py
+pygments/formatters/img.py
+pygments/formatters/latex.py
+pygments/formatters/other.py
+pygments/formatters/rtf.py
+pygments/formatters/svg.py
+pygments/formatters/terminal.py
+pygments/formatters/terminal256.py
+pygments/lexers/__init__.py
+pygments/lexers/_clbuiltins.py
+pygments/lexers/_luabuiltins.py
+pygments/lexers/_mapping.py
+pygments/lexers/_phpbuiltins.py
+pygments/lexers/_vimbuiltins.py
+pygments/lexers/agile.py
+pygments/lexers/asm.py
+pygments/lexers/compiled.py
+pygments/lexers/dotnet.py
+pygments/lexers/functional.py
+pygments/lexers/math.py
+pygments/lexers/other.py
+pygments/lexers/parsers.py
+pygments/lexers/special.py
+pygments/lexers/templates.py
+pygments/lexers/text.py
+pygments/lexers/web.py
+pygments/styles/__init__.py
+pygments/styles/autumn.py
+pygments/styles/borland.py
+pygments/styles/bw.py
+pygments/styles/colorful.py
+pygments/styles/default.py
+pygments/styles/emacs.py
+pygments/styles/friendly.py
+pygments/styles/fruity.py
+pygments/styles/manni.py
+pygments/styles/murphy.py
+pygments/styles/native.py
+pygments/styles/pastie.py
+pygments/styles/perldoc.py
+pygments/styles/tango.py
+pygments/styles/trac.py
+pygments/styles/vim.py
+pygments/styles/vs.py
+scripts/check_sources.py
+scripts/detect_missing_analyse_text.py
+scripts/epydoc.css
+scripts/find_codetags.py
+scripts/find_error.py
+scripts/get_vimkw.py
+scripts/pylintrc
+scripts/reindent.py
+scripts/vim2pygments.py
+tests/old_run.py
+tests/run.py
+tests/support.py
+tests/test_basic_api.py
+tests/test_clexer.py
+tests/test_cmdline.py
+tests/test_examplefiles.py
+tests/test_html_formatter.py
+tests/test_latex_formatter.py
+tests/test_regexlexer.py
+tests/test_token.py
+tests/test_using_api.py
+tests/test_util.py
+tests/dtds/HTML4-f.dtd
+tests/dtds/HTML4-s.dtd
+tests/dtds/HTML4.dcl
+tests/dtds/HTML4.dtd
+tests/dtds/HTML4.soc
+tests/dtds/HTMLlat1.ent
+tests/dtds/HTMLspec.ent
+tests/dtds/HTMLsym.ent
+tests/examplefiles/ANTLRv3.g
+tests/examplefiles/AlternatingGroup.mu
+tests/examplefiles/Constants.mo
+tests/examplefiles/DancingSudoku.lhs
+tests/examplefiles/Errors.scala
+tests/examplefiles/Intro.java
+tests/examplefiles/Makefile
+tests/examplefiles/Object.st
+tests/examplefiles/RegexMatcher.ns2
+tests/examplefiles/SmallCheck.hs
+tests/examplefiles/Sudoku.lhs
+tests/examplefiles/apache2.conf
+tests/examplefiles/as3_test.as
+tests/examplefiles/as3_test2.as
+tests/examplefiles/as3_test3.as
+tests/examplefiles/badcase.java
+tests/examplefiles/batchfile.bat
+tests/examplefiles/boot-9.scm
+tests/examplefiles/ceval.c
+tests/examplefiles/cheetah_example.html
+tests/examplefiles/classes.dylan
+tests/examplefiles/condensed_ruby.rb
+tests/examplefiles/database.pytb
+tests/examplefiles/de.MoinMoin.po
+tests/examplefiles/django_sample.html+django
+tests/examplefiles/dwarf.cw
+tests/examplefiles/erl_session
+tests/examplefiles/escape_semicolon.clj
+tests/examplefiles/evil_regex.js
+tests/examplefiles/example.aspx
+tests/examplefiles/example.c
+tests/examplefiles/example.cpp
+tests/examplefiles/example.lua
+tests/examplefiles/example.moo
+tests/examplefiles/example.pas
+tests/examplefiles/example.rb
+tests/examplefiles/example.rhtml
+tests/examplefiles/example.sh-session
+tests/examplefiles/example.weechatlog
+tests/examplefiles/example.xhtml
+tests/examplefiles/example.xml
+tests/examplefiles/example.yaml
+tests/examplefiles/example2.aspx
+tests/examplefiles/firefox.mak
+tests/examplefiles/format.ml
+tests/examplefiles/functional.rst
+tests/examplefiles/genclass.clj
+tests/examplefiles/genshi_example.xml+genshi
+tests/examplefiles/genshitext_example.genshitext
+tests/examplefiles/glsl.frag
+tests/examplefiles/glsl.vert
+tests/examplefiles/html+php_faulty.php
+tests/examplefiles/irb_heredoc
+tests/examplefiles/jinjadesignerdoc.rst
+tests/examplefiles/lighttpd_config.conf
+tests/examplefiles/linecontinuation.py
+tests/examplefiles/ltmain.sh
+tests/examplefiles/matlab_noreturn
+tests/examplefiles/matlab_sample
+tests/examplefiles/matlabsession_sample.txt
+tests/examplefiles/minimal.ns2
+tests/examplefiles/moin_SyntaxReference.txt
+tests/examplefiles/multiline_regexes.rb
+tests/examplefiles/nasm_aoutso.asm
+tests/examplefiles/nasm_objexe.asm
+tests/examplefiles/nginx_nginx.conf
+tests/examplefiles/numbers.c
+tests/examplefiles/objc_example.m
+tests/examplefiles/objc_example2.m
+tests/examplefiles/perl_perl5db
+tests/examplefiles/perl_regex-delims
+tests/examplefiles/perlfunc.1
+tests/examplefiles/phpcomplete.vim
+tests/examplefiles/pleac.in.rb
+tests/examplefiles/pppoe.applescript
+tests/examplefiles/py3_test.txt
+tests/examplefiles/pycon_test.pycon
+tests/examplefiles/pytb_test2.pytb
+tests/examplefiles/python25-bsd.mak
+tests/examplefiles/qsort.prolog
+tests/examplefiles/ragel-cpp_rlscan
+tests/examplefiles/ragel-cpp_snippet
+tests/examplefiles/regex.js
+tests/examplefiles/ruby_func_def.rb
+tests/examplefiles/sibling.prolog
+tests/examplefiles/simple.md
+tests/examplefiles/smarty_example.html
+tests/examplefiles/source.lgt
+tests/examplefiles/sources.list
+tests/examplefiles/sphere.pov
+tests/examplefiles/sqlite3.sqlite3-console
+tests/examplefiles/squid.conf
+tests/examplefiles/string_delimiters.d
+tests/examplefiles/test.R
+tests/examplefiles/test.bas
+tests/examplefiles/test.boo
+tests/examplefiles/test.cs
+tests/examplefiles/test.css
+tests/examplefiles/test.d
+tests/examplefiles/test.erl
+tests/examplefiles/test.evoque
+tests/examplefiles/test.html
+tests/examplefiles/test.java
+tests/examplefiles/test.jsp
+tests/examplefiles/test.moo
+tests/examplefiles/test.myt
+tests/examplefiles/test.pas
+tests/examplefiles/test.php
+tests/examplefiles/test.plot
+tests/examplefiles/test.r3
+tests/examplefiles/test.rb
+tests/examplefiles/test.rhtml
+tests/examplefiles/test.tcsh
+tests/examplefiles/test.xsl
+tests/examplefiles/type.lisp
+tests/examplefiles/unicode.applescript
+tests/examplefiles/while.pov
+tests/examplefiles/zmlrpc.f90
+tests/examplefiles/broken/fucked_up.rb
\ No newline at end of file
diff --git a/Pygments.egg-info/dependency_links.txt b/Pygments.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Pygments.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/Pygments.egg-info/not-zip-safe b/Pygments.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Pygments.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/Pygments.egg-info/top_level.txt b/Pygments.egg-info/top_level.txt
new file mode 100644
index 0000000..a9f49e0
--- /dev/null
+++ b/Pygments.egg-info/top_level.txt
@@ -0,0 +1 @@
+pygments
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..5873d87
--- /dev/null
+++ b/TODO
@@ -0,0 +1,22 @@
+Todo
+====
+
+suggested new lexers
+--------------------
+
+* IPython sessions
+* Nemerle
+* PostgreSQL/SQLite
+
+for 1.0
+-------
+
+- lexers that need work:
+  * review perl lexer (numerous bugs, but so far no one had complaints ;)
+  * readd property support for C# lexer? that is, find a regex that doesn't
+    backtrack to death...
+  * add support for function name highlighting to C++ lexer
+
+- allow "overlay" token types to highlight specials: nth line, a word etc.
+
+- pygmentize option presets, more sophisticated method to output styles?
diff --git a/docs/build/api.html b/docs/build/api.html
new file mode 100644
index 0000000..ec31e52
--- /dev/null
+++ b/docs/build/api.html
@@ -0,0 +1,458 @@
+
+
+
+  The full Pygments API — Pygments
+  
+  
+
+
+  
+

Pygments

+

The full Pygments API

+ + « Back To Index + + +
+

Contents

+ +
+ + +

This page describes the Pygments API.

+
+

High-level API

+

Functions from the pygments module:

+
+
def lex(code, lexer):
+
Lex code with the lexer (must be a Lexer instance) +and return an iterable of tokens. Currently, this only calls +lexer.get_tokens().
+
def format(tokens, formatter, outfile=None):
+
Format a token stream (iterable of tokens) tokens with the +formatter (must be a Formatter instance). The result is +written to outfile, or if that is None, returned as a +string.
+
def highlight(code, lexer, formatter, outfile=None):
+
This is the most high-level highlighting function. +It combines lex and format in one function.
+
+

Functions from pygments.lexers:

+
+
def get_lexer_by_name(alias, **options):
+

Return an instance of a Lexer subclass that has alias in its +aliases list. The lexer is given the options at its +instantiation.

+

Will raise pygments.util.ClassNotFound if no lexer with that alias is +found.

+
+
def get_lexer_for_filename(fn, **options):
+

Return a Lexer subclass instance that has a filename pattern +matching fn. The lexer is given the options at its +instantiation.

+

Will raise pygments.util.ClassNotFound if no lexer for that filename is +found.

+
+
def get_lexer_for_mimetype(mime, **options):
+

Return a Lexer subclass instance that has mime in its mimetype +list. The lexer is given the options at its instantiation.

+

Will raise pygments.util.ClassNotFound if not lexer for that mimetype is +found.

+
+
def guess_lexer(text, **options):
+

Return a Lexer subclass instance that's guessed from the text +in text. For that, the analyse_text() method of every known +lexer class is called with the text as argument, and the lexer +which returned the highest value will be instantiated and returned.

+

pygments.util.ClassNotFound is raised if no lexer thinks it can handle the +content.

+
+
def guess_lexer_for_filename(filename, text, **options):
+

As guess_lexer(), but only lexers which have a pattern in filenames +or alias_filenames that matches filename are taken into consideration.

+

pygments.util.ClassNotFound is raised if no lexer thinks it can handle the +content.

+
+
def get_all_lexers():
+

Return an iterable over all registered lexers, yielding tuples in the +format:

+
+(longname, tuple of aliases, tuple of filename patterns, tuple of mimetypes)
+
+

New in Pygments 0.6.

+
+
+

Functions from pygments.formatters:

+
+
def get_formatter_by_name(alias, **options):
+

Return an instance of a Formatter subclass that has alias in its +aliases list. The formatter is given the options at its +instantiation.

+

Will raise pygments.util.ClassNotFound if no formatter with that alias is +found.

+
+
def get_formatter_for_filename(fn, **options):
+

Return a Formatter subclass instance that has a filename pattern +matching fn. The formatter is given the options at its +instantiation.

+

Will raise pygments.util.ClassNotFound if no formatter for that filename +is found.

+
+
+

Functions from pygments.styles:

+
+
def get_style_by_name(name):
+

Return a style class by its short name. The names of the builtin styles +are listed in pygments.styles.STYLE_MAP.

+

Will raise pygments.util.ClassNotFound if no style of that name is found.

+
+
def get_all_styles():
+

Return an iterable over all registered styles, yielding their names.

+

New in Pygments 0.6.

+
+
+
+
+

Lexers

+

A lexer (derived from pygments.lexer.Lexer) has the following functions:

+
+
def __init__(self, **options):
+

The constructor. Takes a **keywords dictionary of options. +Every subclass must first process its own options and then call +the Lexer constructor, since it processes the stripnl, +stripall and tabsize options.

+

An example looks like this:

+
def __init__(self, **options):
+    self.compress = options.get('compress', '')
+    Lexer.__init__(self, **options)
+
+

As these options must all be specifiable as strings (due to the +command line usage), there are various utility functions +available to help with that, see Option processing.

+
+
def get_tokens(self, text):
+

This method is the basic interface of a lexer. It is called by +the highlight() function. It must process the text and return an +iterable of (tokentype, value) pairs from text.

+

Normally, you don't need to override this method. The default +implementation processes the stripnl, stripall and tabsize +options and then yields all tokens from get_tokens_unprocessed(), +with the index dropped.

+
+
def get_tokens_unprocessed(self, text):
+

This method should process the text and return an iterable of +(index, tokentype, value) tuples where index is the starting +position of the token within the input text.

+

This method must be overridden by subclasses.

+
+
def analyse_text(text):
+
A static method which is called for lexer guessing. It should analyse +the text and return a float in the range from 0.0 to 1.0. +If it returns 0.0, the lexer will not be selected as the most +probable one, if it returns 1.0, it will be selected immediately.
+
+

For a list of known tokens have a look at the Tokens page.

+

A lexer also can have the following attributes (in fact, they are mandatory +except alias_filenames) that are used by the builtin lookup mechanism.

+
+
name
+
Full name for the lexer, in human-readable form.
+
aliases
+
A list of short, unique identifiers that can be used to lookup +the lexer from a list, e.g. using get_lexer_by_name().
+
filenames
+
A list of fnmatch patterns that match filenames which contain +content for this lexer. The patterns in this list should be unique among +all lexers.
+
alias_filenames
+
A list of fnmatch patterns that match filenames which may or may not +contain content for this lexer. This list is used by the +guess_lexer_for_filename() function, to determine which lexers are +then included in guessing the correct one. That means that e.g. every +lexer for HTML and a template language should include \*.html in +this list.
+
mimetypes
+
A list of MIME types for content that can be lexed with this +lexer.
+
+
+
+

Formatters

+

A formatter (derived from pygments.formatter.Formatter) has the following +functions:

+
+
def __init__(self, **options):
+

As with lexers, this constructor processes options and then must call +the base class __init__.

+

The Formatter class recognizes the options style, full and +title. It is up to the formatter class whether it uses them.

+
+
def get_style_defs(self, arg=''):
+

This method must return statements or declarations suitable to define +the current style for subsequent highlighted text (e.g. CSS classes +in the HTMLFormatter).

+

The optional argument arg can be used to modify the generation and +is formatter dependent (it is standardized because it can be given on +the command line).

+

This method is called by the -S command-line option, the arg +is then given by the -a option.

+
+
def format(self, tokensource, outfile):
+

This method must format the tokens from the tokensource iterable and +write the formatted version to the file object outfile.

+

Formatter options can control how exactly the tokens are converted.

+
+
+

A formatter must have the following attributes that are used by the +builtin lookup mechanism. (New in Pygments 0.7.)

+
+
name
+
Full name for the formatter, in human-readable form.
+
aliases
+
A list of short, unique identifiers that can be used to lookup +the formatter from a list, e.g. using get_formatter_by_name().
+
filenames
+
A list of fnmatch patterns that match filenames for which this formatter +can produce output. The patterns in this list should be unique among +all formatters.
+
+
+
+

Option processing

+

The pygments.util module has some utility functions usable for option +processing:

+
+
class OptionError
+
This exception will be raised by all option processing functions if +the type or value of the argument is not correct.
+
def get_bool_opt(options, optname, default=None):
+

Interpret the key optname from the dictionary options +as a boolean and return it. Return default if optname +is not in options.

+

The valid string values for True are 1, yes, +true and on, the ones for False are 0, +no, false and off (matched case-insensitively).

+
+
def get_int_opt(options, optname, default=None):
+
As get_bool_opt, but interpret the value as an integer.
+
def get_list_opt(options, optname, default=None):
+
If the key optname from the dictionary options is a string, +split it at whitespace and return it. If it is already a list +or a tuple, it is returned as a list.
+
def get_choice_opt(options, optname, allowed, default=None):
+
If the key optname from the dictionary is not in the sequence +allowed, raise an error, otherwise return it. New in Pygments 0.8.
+
+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/authors.html b/docs/build/authors.html new file mode 100644 index 0000000..d6f796d --- /dev/null +++ b/docs/build/authors.html @@ -0,0 +1,274 @@ + + + + Authors — Pygments + + + + +
+

Pygments

+

Authors

+ + « Back To Index + + +

Pygments is written and maintained by Georg Brandl <georg@python.org>.

+

Major developers are Tim Hatch <tim@timhatch.com> and Armin Ronacher +<armin.ronacher@active-4.com>.

+

Other contributors, listed alphabetically, are:

+
    +
  • Kumar Appaiah -- Debian control lexer
  • +
  • Ali Afshar -- image formatter
  • +
  • Andreas Amann -- AppleScript lexer
  • +
  • Stefan Matthias Aust -- Smalltalk lexer
  • +
  • Ben Bangert -- Mako lexers
  • +
  • Max Battcher -- Darcs patch lexer
  • +
  • Michael Bayer -- Myghty lexers
  • +
  • Jarrett Billingsley -- MiniD lexer
  • +
  • Adam Blinkinsop -- Haskell, Redcode lexers
  • +
  • Frits van Bommel -- assembler lexers
  • +
  • Pierre Bourdon -- bugfixes
  • +
  • Christopher Creutzig -- MuPAD lexer
  • +
  • Pete Curry -- bugfixes
  • +
  • Nick Efford -- Python 3 lexer
  • +
  • Artem Egorkine -- terminal256 formatter
  • +
  • Laurent Gautier -- R/S lexer
  • +
  • Krzysiek Goj -- Scala lexer
  • +
  • Matt Good -- Genshi, Cheetah lexers
  • +
  • Matthew Harrison -- SVG formatter
  • +
  • Steven Hazel -- Tcl lexer
  • +
  • Varun Hiremath -- Debian control lexer
  • +
  • Dennis Kaarsemaker -- sources.list lexer
  • +
  • Marek Kubica -- Scheme lexer
  • +
  • Jochen Kupperschmidt -- Markdown processor
  • +
  • Gerd Kurzbach -- Modelica lexer
  • +
  • Mark Lee -- Vala lexer
  • +
  • Kirk McDonald -- D lexer
  • +
  • Lukas Meuser -- BBCode formatter, Lua lexer
  • +
  • Paulo Moura -- Logtalk lexer
  • +
  • Ana Nelson -- Ragel, ANTLR lexers
  • +
  • Jesper Noehr -- HTML formatter "anchorlinenos"
  • +
  • Jonas Obrist -- BBCode lexer
  • +
  • David Oliva -- Rebol lexer
  • +
  • Ronny Pfannschmidt -- BBCode lexer
  • +
  • Benjamin Peterson -- Test suite refactoring
  • +
  • Justin Reidy -- MXML lexer
  • +
  • Andre Roberge -- Tango style
  • +
  • Mario Ruggier -- Evoque lexers
  • +
  • Stou Sandalski -- NumPy, FORTRAN, tcsh and XSLT lexers
  • +
  • Matteo Sasso -- Common Lisp lexer
  • +
  • Ken Schutte -- Matlab lexers
  • +
  • Tassilo Schweyer -- Io, MOOCode lexers
  • +
  • Joerg Sieker -- ABAP lexer
  • +
  • Kirill Simonov -- YAML lexer
  • +
  • Tiberius Teng -- default style overhaul
  • +
  • Jeremy Thurgood -- Erlang, Squid config lexers
  • +
  • Whitney Young -- ObjectiveC lexer
  • +
  • Dietmar Winkler -- Modelica lexer
  • +
  • Nils Winter -- Smalltalk lexer
  • +
  • Davy Wybiral -- Clojure lexer
  • +
+

Many thanks for all contributions!

+ +
+ + + \ No newline at end of file diff --git a/docs/build/changelog.html b/docs/build/changelog.html new file mode 100644 index 0000000..aeffcb3 --- /dev/null +++ b/docs/build/changelog.html @@ -0,0 +1,655 @@ + + + + Changelog — Pygments + + + + +
+

Pygments

+

Changelog

+ + « Back To Index + + + + +

Issue numbers refer to the tracker at http://dev.pocoo.org/projects/pygments/.

+
+

Version 1.1.1

+

(bugfix release, released Sep 15, 2009)

+
    +
  • Fixed the BBCode lexer (#435).
  • +
  • Added support for new Jinja2 keywords.
  • +
  • Fixed test suite failures.
  • +
  • Added Gentoo-specific suffixes to Bash lexer.
  • +
+
+
+

Version 1.1

+

(codename Brillouin, released Sep 11, 2009)

+
    +
  • Ported Pygments to Python 3. This needed a few changes in the way +encodings are handled; they may affect corner cases when used with +Python 2 as well.
  • +
  • Lexers added:
      +
    • Antlr/Ragel, thanks to Ana Nelson
    • +
    • (Ba)sh shell
    • +
    • Erlang shell
    • +
    • GLSL
    • +
    • Prolog
    • +
    • Evoque
    • +
    • Modelica
    • +
    • Rebol
    • +
    • MXML
    • +
    • Cython
    • +
    • ABAP
    • +
    • ASP.net (VB/C#)
    • +
    • Vala
    • +
    • Newspeak
    • +
    +
  • +
  • Fixed the LaTeX formatter's output so that output generated for one style +can be used with the style definitions of another (#384).
  • +
  • Added "anchorlinenos" and "noclobber_cssfile" (#396) options to HTML +formatter.
  • +
  • Support multiline strings in Lua lexer.
  • +
  • Rewrite of the JavaScript lexer by Pumbaa80 to better support regular +expression literals (#403).
  • +
  • When pygmentize is asked to highlight a file for which multiple lexers +match the filename, use the analyse_text guessing engine to determine the +winner (#355).
  • +
  • Fixed minor bugs in the JavaScript lexer (#383), the Matlab lexer (#378), +the Scala lexer (#392), the INI lexer (#391), the Clojure lexer (#387) +and the AS3 lexer (#389).
  • +
  • Fixed three Perl heredoc lexing bugs (#379, #400, #422).
  • +
  • Fixed a bug in the image formatter which misdetected lines (#380).
  • +
  • Fixed bugs lexing extended Ruby strings and regexes.
  • +
  • Fixed a bug when lexing git diffs.
  • +
  • Fixed a bug lexing the empty commit in the PHP lexer (#405).
  • +
  • Fixed a bug causing Python numbers to be mishighlighted as floats (#397).
  • +
  • Fixed a bug when backslashes are used in odd locations in Python (#395).
  • +
  • Fixed various bugs in Matlab and S-Plus lexers, thanks to Winston Chang (#410, +#411, #413, #414) and fmarc (#419).
  • +
  • Fixed a bug in Haskell single-line comment detection (#426).
  • +
  • Added new-style reStructuredText directive for docutils 0.5+ (#428).
  • +
+
+
+

Version 1.0

+

(codename Dreiundzwanzig, released Nov 23, 2008)

+
    +
  • Don't use join(splitlines()) when converting newlines to \n, +because that doesn't keep all newlines at the end when the +stripnl lexer option is False.

    +
  • +
  • Add -N option to command-line interface to get a lexer name +for a given filename.

    +
  • +
  • Add Tango style, written by Andre Roberge for the Crunchy project.

    +
  • +
  • Add Python3TracebackLexer and python3 option to +PythonConsoleLexer.

    +
  • +
  • Fix a few bugs in the Haskell lexer.

    +
  • +
  • Fix PythonTracebackLexer to be able to recognize SyntaxError and +KeyboardInterrupt (#360).

    +
  • +
  • Provide one formatter class per image format, so that surprises like:

    +
    +pygmentize -f gif -o foo.gif foo.py
    +
    +

    creating a PNG file are avoided.

    +
  • +
  • Actually use the font_size option of the image formatter.

    +
  • +
  • Fixed numpy lexer that it doesn't listen for *.py any longer.

    +
  • +
  • Fixed HTML formatter so that text options can be Unicode +strings (#371).

    +
  • +
  • Unified Diff lexer supports the "udiff" alias now.

    +
  • +
  • Fix a few issues in Scala lexer (#367).

    +
  • +
  • RubyConsoleLexer now supports simple prompt mode (#363).

    +
  • +
  • JavascriptLexer is smarter about what constitutes a regex (#356).

    +
  • +
  • Add Applescript lexer, thanks to Andreas Amann (#330).

    +
  • +
  • Make the codetags more strict about matching words (#368).

    +
  • +
  • NginxConfLexer is a little more accurate on mimetypes and +variables (#370).

    +
  • +
+
+
+

Version 0.11.1

+

(released Aug 24, 2008)

+
    +
  • Fix a Jython compatibility issue in pygments.unistring (#358).
  • +
+
+
+

Version 0.11

+

(codename Straußenei, released Aug 23, 2008)

+

Many thanks go to Tim Hatch for writing or integrating most of the bug +fixes and new features.

+
    +
  • Lexers added:
      +
    • Nasm-style assembly language, thanks to delroth
    • +
    • YAML, thanks to Kirill Simonov
    • +
    • ActionScript 3, thanks to Pierre Bourdon
    • +
    • Cheetah/Spitfire templates, thanks to Matt Good
    • +
    • Lighttpd config files
    • +
    • Nginx config files
    • +
    • Gnuplot plotting scripts
    • +
    • Clojure
    • +
    • POV-Ray scene files
    • +
    • Sqlite3 interactive console sessions
    • +
    • Scala source files, thanks to Krzysiek Goj
    • +
    +
  • +
  • Lexers improved:
      +
    • C lexer highlights standard library functions now and supports C99 +types.
    • +
    • Bash lexer now correctly highlights heredocs without preceding +whitespace.
    • +
    • Vim lexer now highlights hex colors properly and knows a couple +more keywords.
    • +
    • Irc logs lexer now handles xchat's default time format (#340) and +correctly highlights lines ending in >.
    • +
    • Support more delimiters for perl regular expressions (#258).
    • +
    • ObjectiveC lexer now supports 2.0 features.
    • +
    +
  • +
  • Added "Visual Studio" style.
  • +
  • Updated markdown processor to Markdown 1.7.
  • +
  • Support roman/sans/mono style defs and use them in the LaTeX +formatter.
  • +
  • The RawTokenFormatter is no longer registered to *.raw and it's +documented that tokenization with this lexer may raise exceptions.
  • +
  • New option hl_lines to HTML formatter, to highlight certain +lines.
  • +
  • New option prestyles to HTML formatter.
  • +
  • New option -g to pygmentize, to allow lexer guessing based on +filetext (can be slowish, so file extensions are still checked +first).
  • +
  • guess_lexer() now makes its decision much faster due to a cache +of whether data is xml-like (a check which is used in several +versions of analyse_text(). Several lexers also have more +accurate analyse_text() now.
  • +
+
+
+

Version 0.10

+

(codename Malzeug, released May 06, 2008)

+
    +
  • Lexers added:
      +
    • Io
    • +
    • Smalltalk
    • +
    • Darcs patches
    • +
    • Tcl
    • +
    • Matlab
    • +
    • Matlab sessions
    • +
    • FORTRAN
    • +
    • XSLT
    • +
    • tcsh
    • +
    • NumPy
    • +
    • Python 3
    • +
    • S, S-plus, R statistics languages
    • +
    • Logtalk
    • +
    +
  • +
  • In the LatexFormatter, the commandprefix option is now by default +'PY' instead of 'C', since the latter resulted in several collisions +with other packages. Also, the special meaning of the arg +argument to get_style_defs() was removed.
  • +
  • Added ImageFormatter, to format code as PNG, JPG, GIF or BMP. +(Needs the Python Imaging Library.)
  • +
  • Support doc comments in the PHP lexer.
  • +
  • Handle format specifications in the Perl lexer.
  • +
  • Fix comment handling in the Batch lexer.
  • +
  • Add more file name extensions for the C++, INI and XML lexers.
  • +
  • Fixes in the IRC and MuPad lexers.
  • +
  • Fix function and interface name highlighting in the Java lexer.
  • +
  • Fix at-rule handling in the CSS lexer.
  • +
  • Handle KeyboardInterrupts gracefully in pygmentize.
  • +
  • Added BlackWhiteStyle.
  • +
  • Bash lexer now correctly highlights math, does not require +whitespace after semicolons, and correctly highlights boolean +operators.
  • +
  • Makefile lexer is now capable of handling BSD and GNU make syntax.
  • +
+
+
+

Version 0.9

+

(codename Herbstzeitlose, released Oct 14, 2007)

+
    +
  • Lexers added:
      +
    • Erlang
    • +
    • ActionScript
    • +
    • Literate Haskell
    • +
    • Common Lisp
    • +
    • Various assembly languages
    • +
    • Gettext catalogs
    • +
    • Squid configuration
    • +
    • Debian control files
    • +
    • MySQL-style SQL
    • +
    • MOOCode
    • +
    +
  • +
  • Lexers improved:
      +
    • Greatly improved the Haskell and OCaml lexers.
    • +
    • Improved the Bash lexer's handling of nested constructs.
    • +
    • The C# and Java lexers exhibited abysmal performance with some +input code; this should now be fixed.
    • +
    • The IRC logs lexer is now able to colorize weechat logs too.
    • +
    • The Lua lexer now recognizes multi-line comments.
    • +
    • Fixed bugs in the D and MiniD lexer.
    • +
    +
  • +
  • The encoding handling of the command line mode (pygmentize) was +enhanced. You shouldn't get UnicodeErrors from it anymore if you +don't give an encoding option.
  • +
  • Added a -P option to the command line mode which can be used to +give options whose values contain commas or equals signs.
  • +
  • Added 256-color terminal formatter.
  • +
  • Added an experimental SVG formatter.
  • +
  • Added the lineanchors option to the HTML formatter, thanks to +Ian Charnas for the idea.
  • +
  • Gave the line numbers table a CSS class in the HTML formatter.
  • +
  • Added a Vim 7-like style.
  • +
+
+
+

Version 0.8.1

+

(released Jun 27, 2007)

+
    +
  • Fixed POD highlighting in the Ruby lexer.
  • +
  • Fixed Unicode class and namespace name highlighting in the C# lexer.
  • +
  • Fixed Unicode string prefix highlighting in the Python lexer.
  • +
  • Fixed a bug in the D and MiniD lexers.
  • +
  • Fixed the included MoinMoin parser.
  • +
+
+
+

Version 0.8

+

(codename Maikäfer, released May 30, 2007)

+
    +
  • Lexers added:
      +
    • Haskell, thanks to Adam Blinkinsop
    • +
    • Redcode, thanks to Adam Blinkinsop
    • +
    • D, thanks to Kirk McDonald
    • +
    • MuPad, thanks to Christopher Creutzig
    • +
    • MiniD, thanks to Jarrett Billingsley
    • +
    • Vim Script, by Tim Hatch
    • +
    +
  • +
  • The HTML formatter now has a second line-numbers mode in which it +will just integrate the numbers in the same <pre> tag as the +code.
  • +
  • The CSharpLexer now is Unicode-aware, which means that it has an +option that can be set so that it correctly lexes Unicode +identifiers allowed by the C# specs.
  • +
  • Added a RaiseOnErrorTokenFilter that raises an exception when the +lexer generates an error token, and a VisibleWhitespaceFilter that +converts whitespace (spaces, tabs, newlines) into visible +characters.
  • +
  • Fixed the do_insertions() helper function to yield correct +indices.
  • +
  • The ReST lexer now automatically highlights source code blocks in +".. sourcecode:: language" and ".. code:: language" directive +blocks.
  • +
  • Improved the default style (thanks to Tiberius Teng). The old +default is still available as the "emacs" style (which was an alias +before).
  • +
  • The get_style_defs method of HTML formatters now uses the +cssclass option as the default selector if it was given.
  • +
  • Improved the ReST and Bash lexers a bit.
  • +
  • Fixed a few bugs in the Makefile and Bash lexers, thanks to Tim +Hatch.
  • +
  • Fixed a bug in the command line code that disallowed -O options +when using the -S option.
  • +
  • Fixed a bug in the RawTokenFormatter.
  • +
+
+
+

Version 0.7.1

+

(released Feb 15, 2007)

+
    +
  • Fixed little highlighting bugs in the Python, Java, Scheme and +Apache Config lexers.
  • +
  • Updated the included manpage.
  • +
  • Included a built version of the documentation in the source tarball.
  • +
+
+
+

Version 0.7

+

(codename Faschingskrapfn, released Feb 14, 2007)

+
    +
  • Added a MoinMoin parser that uses Pygments. With it, you get +Pygments highlighting in Moin Wiki pages.
  • +
  • Changed the exception raised if no suitable lexer, formatter etc. is +found in one of the get_*_by_* functions to a custom exception, +pygments.util.ClassNotFound. It is, however, a subclass of +ValueError in order to retain backwards compatibility.
  • +
  • Added a -H command line option which can be used to get the +docstring of a lexer, formatter or filter.
  • +
  • Made the handling of lexers and formatters more consistent. The +aliases and filename patterns of formatters are now attributes on +them.
  • +
  • Added an OCaml lexer, thanks to Adam Blinkinsop.
  • +
  • Made the HTML formatter more flexible, and easily subclassable in +order to make it easy to implement custom wrappers, e.g. alternate +line number markup. See the documentation.
  • +
  • Added an outencoding option to all formatters, making it possible +to override the encoding (which is used by lexers and formatters) +when using the command line interface. Also, if using the terminal +formatter and the output file is a terminal and has an encoding +attribute, use it if no encoding is given.
  • +
  • Made it possible to just drop style modules into the styles +subpackage of the Pygments installation.
  • +
  • Added a "state" keyword argument to the using helper.
  • +
  • Added a commandprefix option to the LatexFormatter which allows +to control how the command names are constructed.
  • +
  • Added quite a few new lexers, thanks to Tim Hatch:
      +
    • Java Server Pages
    • +
    • Windows batch files
    • +
    • Trac Wiki markup
    • +
    • Python tracebacks
    • +
    • ReStructuredText
    • +
    • Dylan
    • +
    • and the Befunge esoteric programming language (yay!)
    • +
    +
  • +
  • Added Mako lexers by Ben Bangert.
  • +
  • Added "fruity" style, another dark background originally vim-based +theme.
  • +
  • Added sources.list lexer by Dennis Kaarsemaker.
  • +
  • Added token stream filters, and a pygmentize option to use them.
  • +
  • Changed behavior of in Operator for tokens.
  • +
  • Added mimetypes for all lexers.
  • +
  • Fixed some problems lexing Python strings.
  • +
  • Fixed tickets: #167, #178, #179, #180, #185, #201.
  • +
+
+
+

Version 0.6

+

(codename Zimtstern, released Dec 20, 2006)

+
    +
  • Added option for the HTML formatter to write the CSS to an external +file in "full document" mode.
  • +
  • Added RTF formatter.
  • +
  • Added Bash and Apache configuration lexers (thanks to Tim Hatch).
  • +
  • Improved guessing methods for various lexers.
  • +
  • Added @media support to CSS lexer (thanks to Tim Hatch).
  • +
  • Added a Groff lexer (thanks to Tim Hatch).
  • +
  • License change to BSD.
  • +
  • Added lexers for the Myghty template language.
  • +
  • Added a Scheme lexer (thanks to Marek Kubica).
  • +
  • Added some functions to iterate over existing lexers, formatters and +lexers.
  • +
  • The HtmlFormatter's get_style_defs() can now take a list as an +argument to generate CSS with multiple prefixes.
  • +
  • Support for guessing input encoding added.
  • +
  • Encoding support added: all processing is now done with Unicode +strings, input and output are converted from and optionally to byte +strings (see the encoding option of lexers and formatters).
  • +
  • Some improvements in the C(++) lexers handling comments and line +continuations.
  • +
+
+
+

Version 0.5.1

+

(released Oct 30, 2006)

+
    +
  • Fixed traceback in pygmentize -L (thanks to Piotr Ozarowski).
  • +
+
+
+

Version 0.5

+

(codename PyKleur, released Oct 30, 2006)

+
    +
  • Initial public release.
  • +
+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/cmdline.html b/docs/build/cmdline.html new file mode 100644 index 0000000..676749b --- /dev/null +++ b/docs/build/cmdline.html @@ -0,0 +1,358 @@ + + + + Command Line Interface — Pygments + + + + +
+

Pygments

+

Command Line Interface

+ + « Back To Index + + + + + +

You can use Pygments from the shell, provided you installed the pygmentize +script:

+
+$ pygmentize test.py
+print "Hello World"
+
+

will print the file test.py to standard output, using the Python lexer +(inferred from the file name extension) and the terminal formatter (because +you didn't give an explicit formatter name).

+

If you want HTML output:

+
+$ pygmentize -f html -l python -o test.html test.py
+
+

As you can see, the -l option explicitly selects a lexer. As seen above, if you +give an input file name and it has an extension that Pygments recognizes, you can +omit this option.

+

The -o option gives an output file name. If it is not given, output is +written to stdout.

+

The -f option selects a formatter (as with -l, it can also be omitted +if an output file name is given and has a supported extension). +If no output file name is given and -f is omitted, the +TerminalFormatter is used.

+

The above command could therefore also be given as:

+
+$ pygmentize -o test.html test.py
+
+
+

Options and filters

+

Lexer and formatter options can be given using the -O option:

+
+$ pygmentize -f html -O style=colorful,linenos=1 -l python test.py
+
+

Be sure to enclose the option string in quotes if it contains any special shell +characters, such as spaces or expansion wildcards like *. If an option +expects a list value, separate the list entries with spaces (you'll have to +quote the option value in this case too, so that the shell doesn't split it).

+

Since the -O option argument is split at commas and expects the split values +to be of the form name=value, you can't give an option value that contains +commas or equals signs. Therefore, an option -P is provided (as of Pygments +0.9) that works like -O but can only pass one option per -P. Its value +can then contain all characters:

+
+$ pygmentize -P "heading=Pygments, the Python highlighter" ...
+
+

Filters are added to the token stream using the -F option:

+
+$ pygmentize -f html -l pascal -F keywordcase:case=upper main.pas
+
+

As you see, options for the filter are given after a colon. As for -O, the +filter name and options must be one shell word, so there may not be any spaces +around the colon.

+
+
+

Generating styles

+

Formatters normally don't output full style information. For example, the HTML +formatter by default only outputs <span> tags with class attributes. +Therefore, there's a special -S option for generating style definitions. +Usage is as follows:

+
+$ pygmentize -f html -S colorful -a .syntax
+
+

generates a CSS style sheet (because you selected the HTML formatter) for +the "colorful" style prepending a ".syntax" selector to all style rules.

+

For an explanation what -a means for a particular formatter, look for +the arg argument for the formatter's get_style_defs() method.

+
+
+

Getting lexer names

+

New in Pygments 1.0.

+

The -N option guesses a lexer name for a given filename, so that

+
+$ pygmentize -N setup.py
+
+

will print out python. It won't highlight anything yet. If no specific +lexer is known for that filename, text is printed.

+
+
+

Getting help

+

The -L option lists lexers, formatters, along with their short +names and supported file name extensions, styles and filters. If you want to see +only one category, give it as an argument:

+
+$ pygmentize -L filters
+
+

will list only all installed filters.

+

The -H option will give you detailed information (the same that can be found +in this documentation) about a lexer, formatter or filter. Usage is as follows:

+
+$ pygmentize -H formatter html
+
+

will print the help for the HTML formatter, while

+
+$ pygmentize -H lexer python
+
+

will print the help for the Python lexer, etc.

+
+
+

Examples

+

Create a full HTML document, including line numbers and stylesheet (using the +"emacs" style), highlighting the Python file setup.py to setup.html:

+
+$ pygmentize -O full,style=emacs -o setup.html setup.py
+
+
+
+

A note on encodings

+

New in Pygments 0.9.

+

Pygments tries to be smart regarding encodings in the formatting process:

+
    +
  • If you give an encoding option, it will be used as the input and +output encoding.
  • +
  • If you give an outencoding option, it will override encoding +as the output encoding.
  • +
  • If you don't give an encoding and have given an output file, the default +encoding for lexer and formatter is latin1 (which will pass through +all non-ASCII characters).
  • +
  • If you don't give an encoding and haven't given an output file (that means +output is written to the console), the default encoding for lexer and +formatter is the terminal encoding (sys.stdout.encoding).
  • +
+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/filterdevelopment.html b/docs/build/filterdevelopment.html new file mode 100644 index 0000000..edc07a0 --- /dev/null +++ b/docs/build/filterdevelopment.html @@ -0,0 +1,282 @@ + + + + Write your own filter — Pygments + + + + +
+

Pygments

+

Write your own filter

+ + « Back To Index + + +
+

Contents

+ +
+ + +

New in Pygments 0.7.

+

Writing own filters is very easy. All you have to do is to subclass +the Filter class and override the filter method. Additionally a +filter is instanciated with some keyword arguments you can use to +adjust the behavior of your filter.

+
+

Subclassing Filters

+

As an example, we write a filter that converts all Name.Function tokens +to normal Name tokens to make the output less colorful.

+
from pygments.util import get_bool_opt
+from pygments.token import Name
+from pygments.filter import Filter
+
+class UncolorFilter(Filter):
+
+    def __init__(self, **options):
+        Filter.__init__(self, **options)
+        self.class_too = get_bool_opt(options, 'classtoo')
+
+    def filter(self, lexer, stream):
+        for ttype, value in stream:
+            if ttype is Name.Function or (self.class_too and
+                                          ttype is Name.Class):
+                ttype = Name
+            yield ttype, value
+
+

Some notes on the lexer argument: that can be quite confusing since it doesn't +need to be a lexer instance. If a filter was added by using the add_filter() +function of lexers, that lexer is registered for the filter. In that case +lexer will refer to the lexer that has registered the filter. It can be used +to access options passed to a lexer. Because it could be None you always have +to check for that case if you access it.

+
+
+

Using a decorator

+

You can also use the simplefilter decorator from the pygments.filter module:

+
from pygments.util import get_bool_opt
+from pygments.token import Name
+from pygments.filter import simplefilter
+
+
+@simplefilter
+def uncolor(lexer, stream, options):
+    class_too = get_bool_opt(options, 'classtoo')
+    for ttype, value in stream:
+        if ttype is Name.Function or (class_too and
+                                      ttype is Name.Class):
+            ttype = Name
+        yield ttype, value
+
+

The decorator automatically subclasses an internal filter class and uses the +decorated function for filtering.

+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/filters.html b/docs/build/filters.html new file mode 100644 index 0000000..4413aec --- /dev/null +++ b/docs/build/filters.html @@ -0,0 +1,377 @@ + + + + Filters — Pygments + + + + +
+

Pygments

+

Filters

+ + « Back To Index + + +
+

Contents

+ +
+ + +

New in Pygments 0.7.

+

You can filter token streams coming from lexers to improve or annotate the +output. For example, you can highlight special words in comments, convert +keywords to upper or lowercase to enforce a style guide etc.

+

To apply a filter, you can use the add_filter() method of a lexer:

+
>>> from pygments.lexers import PythonLexer
+>>> l = PythonLexer()
+>>> # add a filter given by a string and options
+>>> l.add_filter('codetagify', case='lower')
+>>> l.filters
+[<pygments.filters.CodeTagFilter object at 0xb785decc>]
+>>> from pygments.filters import KeywordCaseFilter
+>>> # or give an instance
+>>> l.add_filter(KeywordCaseFilter(case='lower'))
+
+

The add_filter() method takes keyword arguments which are forwarded to +the constructor of the filter.

+

To get a list of all registered filters by name, you can use the +get_all_filters() function from the pygments.filters module that returns an +iterable for all known filters.

+

If you want to write your own filter, have a look at Write your own filter.

+
+

Builtin Filters

+

NameHighlightFilter

+
+

Highlight a normal Name token with a different token type.

+

Example:

+
+filter = NameHighlightFilter(
+    names=['foo', 'bar', 'baz'],
+    tokentype=Name.Function,
+)
+
+

This would highlight the names "foo", "bar" and "baz" +as functions. Name.Function is the default token type.

+

Options accepted:

+
+
names : list of strings
+
A list of names that should be given the different token type. +There is no default.
+
tokentype : TokenType or string
+
A token type or a string containing a token type name that is +used for highlighting the strings in names. The default is +Name.Function.
+
+ +++ + + + +
Name:highlight
+
+

CodeTagFilter

+
+

Highlight special code tags in comments and docstrings.

+

Options accepted:

+
+
codetags : list of strings
+
A list of strings that are flagged as code tags. The default is to +highlight XXX, TODO, BUG and NOTE.
+
+ +++ + + + +
Name:codetagify
+
+

RaiseOnErrorTokenFilter

+
+

Raise an exception when the lexer generates an error token.

+

Options accepted:

+
+
excclass : Exception class
+
The exception class to raise. +The default is pygments.filters.ErrorToken.
+
+

New in Pygments 0.8.

+ +++ + + + +
Name:raiseonerror
+
+

KeywordCaseFilter

+
+

Convert keywords to lowercase or uppercase or capitalize them, which +means first letter uppercase, rest lowercase.

+

This can be useful e.g. if you highlight Pascal code and want to adapt the +code to your styleguide.

+

Options accepted:

+
+
case : string
+
The casing to convert keywords to. Must be one of 'lower', +'upper' or 'capitalize'. The default is 'lower'.
+
+ +++ + + + +
Name:keywordcase
+
+

VisibleWhitespaceFilter

+
+

Convert tabs, newlines and/or spaces to visible characters.

+

Options accepted:

+
+
spaces : string or bool
+
If this is a one-character string, spaces will be replaces by this string. +If it is another true value, spaces will be replaced by · (unicode +MIDDLE DOT). If it is a false value, spaces will not be replaced. The +default is False.
+
tabs : string or bool
+
The same as for spaces, but the default replacement character is » +(unicode RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK). The default value +is False. Note: this will not work if the tabsize option for the +lexer is nonzero, as tabs will already have been expanded then.
+
tabsize : int
+
If tabs are to be replaced by this filter (see the tabs option), this +is the total number of characters that a tab should be expanded to. +The default is 8.
+
newlines : string or bool
+
The same as for spaces, but the default replacement character is ¶ +(unicode PILCROW SIGN). The default value is False.
+
wstokentype : bool
+
If true, give whitespace the special Whitespace token type. This allows +styling the visible whitespace differently (e.g. greyed out), but it can +disrupt background colors. The default is True.
+
+

New in Pygments 0.8.

+ +++ + + + +
Name:whitespace
+
+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/formatterdevelopment.html b/docs/build/formatterdevelopment.html new file mode 100644 index 0000000..d0a0002 --- /dev/null +++ b/docs/build/formatterdevelopment.html @@ -0,0 +1,374 @@ + + + + Write your own formatter — Pygments + + + + +
+

Pygments

+

Write your own formatter

+ + « Back To Index + + + + + +

As well as creating your own lexer, writing a new +formatter for Pygments is easy and straightforward.

+

A formatter is a class that is initialized with some keyword arguments (the +formatter options) and that must provides a format() method. +Additionally a formatter should provide a get_style_defs() method that +returns the style definitions from the style in a form usable for the +formatter's output format.

+
+

Quickstart

+

The most basic formatter shipped with Pygments is the NullFormatter. It just +sends the value of a token to the output stream:

+
from pygments.formatter import Formatter
+
+class NullFormatter(Formatter):
+    def format(self, tokensource, outfile):
+        for ttype, value in tokensource:
+            outfile.write(value)
+
+

As you can see, the format() method is passed two parameters: tokensource +and outfile. The first is an iterable of (token_type, value) tuples, +the latter a file like object with a write() method.

+

Because the formatter is that basic it doesn't overwrite the get_style_defs() +method.

+
+
+

Styles

+

Styles aren't instantiated but their metaclass provides some class functions +so that you can access the style definitions easily.

+

Styles are iterable and yield tuples in the form (ttype, d) where ttype +is a token and d is a dict with the following keys:

+
+
'color'
+
Hexadecimal color value (eg: 'ff0000' for red) or None if not +defined.
+
'bold'
+
True if the value should be bold
+
'italic'
+
True if the value should be italic
+
'underline'
+
True if the value should be underlined
+
'bgcolor'
+
Hexadecimal color value for the background (eg: 'eeeeeee' for light +gray) or None if not defined.
+
'border'
+
Hexadecimal color value for the border (eg: '0000aa' for a dark +blue) or None for no border.
+
+

Additional keys might appear in the future, formatters should ignore all keys +they don't support.

+
+
+

HTML 3.2 Formatter

+

For an more complex example, let's implement a HTML 3.2 Formatter. We don't +use CSS but inline markup (<u>, <font>, etc). Because this isn't good +style this formatter isn't in the standard library ;-)

+
from pygments.formatter import Formatter
+
+class OldHtmlFormatter(Formatter):
+
+    def __init__(self, **options):
+        Formatter.__init__(self, **options)
+
+        # create a dict of (start, end) tuples that wrap the
+        # value of a token so that we can use it in the format
+        # method later
+        self.styles = {}
+
+        # we iterate over the `_styles` attribute of a style item
+        # that contains the parsed style values.
+        for token, style in self.style:
+            start = end = ''
+            # a style item is a tuple in the following form:
+            # colors are readily specified in hex: 'RRGGBB'
+            if style['color']:
+                start += '<font color="#%s">' % style['color']
+                end = '</font>' + end
+            if style['bold']:
+                start += '<b>'
+                end = '</b>' + end
+            if style['italic']:
+                start += '<i>'
+                end = '</i>' + end
+            if style['underline']:
+                start += '<u>'
+                end = '</u>' + end
+            self.styles[token] = (start, end)
+
+    def format(self, tokensource, outfile):
+        # lastval is a string we use for caching
+        # because it's possible that an lexer yields a number
+        # of consecutive tokens with the same token type.
+        # to minimize the size of the generated html markup we
+        # try to join the values of same-type tokens here
+        lastval = ''
+        lasttype = None
+
+        # wrap the whole output with <pre>
+        outfile.write('<pre>')
+
+        for ttype, value in tokensource:
+            # if the token type doesn't exist in the stylemap
+            # we try it with the parent of the token type
+            # eg: parent of Token.Literal.String.Double is
+            # Token.Literal.String
+            while ttype not in self.styles:
+                ttype = ttype.parent
+            if ttype == lasttype:
+                # the current token type is the same of the last
+                # iteration. cache it
+                lastval += value
+            else:
+                # not the same token as last iteration, but we
+                # have some data in the buffer. wrap it with the
+                # defined style and write it to the output file
+                if lastval:
+                    stylebegin, styleend = self.styles[lasttype]
+                    outfile.write(stylebegin + lastval + styleend)
+                # set lastval/lasttype to current values
+                lastval = value
+                lasttype = ttype
+
+        # if something is left in the buffer, write it to the
+        # output file, then close the opened <pre> tag
+        if lastval:
+            stylebegin, styleend = self.styles[lasttype]
+            outfile.write(stylebegin + lastval + styleend)
+        outfile.write('</pre>\n')
+
+

The comments should explain it. Again, this formatter doesn't override the +get_style_defs() method. If we would have used CSS classes instead of +inline HTML markup, we would need to generate the CSS first. For that +purpose the get_style_defs() method exists:

+
+
+

Generating Style Definitions

+

Some formatters like the LatexFormatter and the HtmlFormatter don't +output inline markup but reference either macros or css classes. Because +the definitions of those are not part of the output, the get_style_defs() +method exists. It is passed one parameter (if it's used and how it's used +is up to the formatter) and has to return a string or None.

+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/formatters.html b/docs/build/formatters.html new file mode 100644 index 0000000..6a64f2d --- /dev/null +++ b/docs/build/formatters.html @@ -0,0 +1,888 @@ + + + + Available formatters — Pygments + + + + +
+

Pygments

+

Available formatters

+ + « Back To Index + + +
+

Contents

+ +
+ + +

This page lists all builtin formatters.

+
+

Common options

+

All formatters support these options:

+
+
encoding
+

New in Pygments 0.6.

+

If given, must be an encoding name (such as "utf-8"). This will +be used to convert the token strings (which are Unicode strings) +to byte strings in the output (default: None). +It will also be written in an encoding declaration suitable for the +document format if the full option is given (e.g. a meta +content-type directive in HTML or an invocation of the inputenc +package in LaTeX).

+

If this is "" or None, Unicode strings will be written +to the output file, which most file-like objects do not support. +For example, pygments.highlight() will return a Unicode string if +called with no outfile argument and a formatter that has encoding +set to None because it uses a StringIO.StringIO object that +supports Unicode arguments to write(). Using a regular file object +wouldn't work.

+
+
outencoding
+

New in Pygments 0.7.

+

When using Pygments from the command line, any encoding option given is +passed to the lexer and the formatter. This is sometimes not desirable, +for example if you want to set the input encoding to "guess". +Therefore, outencoding has been introduced which overrides encoding +for the formatter if given.

+
+
+
+
+

Formatter classes

+

All these classes are importable from pygments.formatters.

+
+

BBCodeFormatter

+
+

Format tokens with BBcodes. These formatting codes are used by many +bulletin boards, so you can highlight your sourcecode with pygments before +posting it there.

+

This formatter has no support for background colors and borders, as there +are no common BBcode tags for that.

+

Some board systems (e.g. phpBB) don't support colors in their [code] tag, +so you can't use the highlighting together with that tag. +Text in a [code] tag usually is shown with a monospace font (which this +formatter can do with the monofont option) and no spaces (which you +need for indentation) are removed.

+

Additional options accepted:

+
+
style
+
The style to use, can be a string or a Style subclass (default: +'default').
+
codetag
+
If set to true, put the output into [code] tags (default: +false)
+
monofont
+
If set to true, add a tag to show the code with a monospace font +(default: false).
+
+ +++ + + + + + +
Short names:bbcode, bb
Filename patterns:None
+
+
+
+

BmpImageFormatter

+
+

Create a bitmap image from source code. This uses the Python Imaging Library to +generate a pixmap from the source code.

+

New in Pygments 1.0. (You could create bitmap images before by passing a +suitable image_format option to the ImageFormatter.)

+ +++ + + + + + +
Short names:bmp, bitmap
Filename patterns:*.bmp
+
+
+
+

GifImageFormatter

+
+

Create a GIF image from source code. This uses the Python Imaging Library to +generate a pixmap from the source code.

+

New in Pygments 1.0. (You could create GIF images before by passing a +suitable image_format option to the ImageFormatter.)

+ +++ + + + + + +
Short names:gif
Filename patterns:*.gif
+
+
+
+

HtmlFormatter

+
+

Format tokens as HTML 4 <span> tags within a <pre> tag, wrapped +in a <div> tag. The <div>'s CSS class can be set by the cssclass +option.

+

If the linenos option is set to "table", the <pre> is +additionally wrapped inside a <table> which has one row and two +cells: one containing the line numbers and one containing the code. +Example:

+
<div class="highlight" >
+<table><tr>
+  <td class="linenos" title="click to toggle"
+    onclick="with (this.firstChild.style)
+             { display = (display == '') ? 'none' : '' }">
+    <pre>1
+    2</pre>
+  </td>
+  <td class="code">
+    <pre><span class="Ke">def </span><span class="NaFu">foo</span>(bar):
+      <span class="Ke">pass</span>
+    </pre>
+  </td>
+</tr></table></div>
+
+

(whitespace added to improve clarity).

+

Wrapping can be disabled using the nowrap option.

+

A list of lines can be specified using the hl_lines option to make these +lines highlighted (as of Pygments 0.11).

+

With the full option, a complete HTML 4 document is output, including +the style definitions inside a <style> tag, or in a separate file if +the cssfile option is given.

+

The get_style_defs(arg='') method of a HtmlFormatter returns a string +containing CSS rules for the CSS classes used by the formatter. The +argument arg can be used to specify additional CSS selectors that +are prepended to the classes. A call fmter.get_style_defs('td .code') +would result in the following CSS classes:

+
td .code .kw { font-weight: bold; color: #00FF00 }
+td .code .cm { color: #999999 }
+...
+
+

If you have Pygments 0.6 or higher, you can also pass a list or tuple to the +get_style_defs() method to request multiple prefixes for the tokens:

+
formatter.get_style_defs(['div.syntax pre', 'pre.syntax'])
+
+

The output would then look like this:

+
div.syntax pre .kw,
+pre.syntax .kw { font-weight: bold; color: #00FF00 }
+div.syntax pre .cm,
+pre.syntax .cm { color: #999999 }
+...
+
+

Additional options accepted:

+
+
nowrap
+
If set to True, don't wrap the tokens at all, not even inside a <pre> +tag. This disables most other options (default: False).
+
full
+
Tells the formatter to output a "full" document, i.e. a complete +self-contained document (default: False).
+
title
+
If full is true, the title that should be used to caption the +document (default: '').
+
style
+
The style to use, can be a string or a Style subclass (default: +'default'). This option has no effect if the cssfile +and noclobber_cssfile option are given and the file specified in +cssfile exists.
+
noclasses
+
If set to true, token <span> tags will not use CSS classes, but +inline styles. This is not recommended for larger pieces of code since +it increases output size by quite a bit (default: False).
+
classprefix
+
Since the token types use relatively short class names, they may clash +with some of your own class names. In this case you can use the +classprefix option to give a string to prepend to all Pygments-generated +CSS class names for token types. +Note that this option also affects the output of get_style_defs().
+
cssclass
+

CSS class for the wrapping <div> tag (default: 'highlight'). +If you set this option, the default selector for get_style_defs() +will be this class.

+

New in Pygments 0.9: If you select the 'table' line numbers, the +wrapping table will have a CSS class of this string plus 'table', +the default is accordingly 'highlighttable'.

+
+
cssstyles
+
Inline CSS styles for the wrapping <div> tag (default: '').
+
prestyles
+
Inline CSS styles for the <pre> tag (default: ''). New in +Pygments 0.11.
+
cssfile
+
If the full option is true and this option is given, it must be the +name of an external file. If the filename does not include an absolute +path, the file's path will be assumed to be relative to the main output +file's path, if the latter can be found. The stylesheet is then written +to this file instead of the HTML file. New in Pygments 0.6.
+
noclobber_cssfile
+
If cssfile is given and the specified file exists, the css file will +not be overwritten. This allows the use of the full option in +combination with a user specified css file. Default is False. +New in Pygments 1.1.
+
linenos
+

If set to 'table', output line numbers as a table with two cells, +one containing the line numbers, the other the whole code. This is +copy-and-paste-friendly, but may cause alignment problems with some +browsers or fonts. If set to 'inline', the line numbers will be +integrated in the <pre> tag that contains the code (that setting +is new in Pygments 0.8).

+

For compatibility with Pygments 0.7 and earlier, every true value +except 'inline' means the same as 'table' (in particular, that +means also True).

+

The default value is False, which means no line numbers at all.

+

Note: with the default ("table") line number mechanism, the line +numbers and code can have different line heights in Internet Explorer +unless you give the enclosing <pre> tags an explicit line-height +CSS property (you get the default line spacing with line-height: +125%).

+
+
hl_lines
+
Specify a list of lines to be highlighted. New in Pygments 0.11.
+
linenostart
+
The line number for the first line (default: 1).
+
linenostep
+
If set to a number n > 1, only every nth line number is printed.
+
linenospecial
+
If set to a number n > 0, every nth line number is given the CSS +class "special" (default: 0).
+
nobackground
+
If set to True, the formatter won't output the background color +for the wrapping element (this automatically defaults to False +when there is no wrapping element [eg: no argument for the +get_syntax_defs method given]) (default: False). New in +Pygments 0.6.
+
lineseparator
+
This string is output between lines of code. It defaults to "\n", +which is enough to break a line inside <pre> tags, but you can +e.g. set it to "<br>" to get HTML line breaks. New in Pygments +0.7.
+
lineanchors
+
If set to a nonempty string, e.g. foo, the formatter will wrap each +output line in an anchor tag with a name of foo-linenumber. +This allows easy linking to certain lines. New in Pygments 0.9.
+
anchorlinenos
+
If set to True, will wrap line numbers in <a> tags. Used in +combination with linenos and lineanchors.
+
+

Subclassing the HTML formatter

+

New in Pygments 0.7.

+

The HTML formatter is now built in a way that allows easy subclassing, thus +customizing the output HTML code. The format() method calls +self._format_lines() which returns a generator that yields tuples of (1, +line), where the 1 indicates that the line is a line of the +formatted source code.

+

If the nowrap option is set, the generator is the iterated over and the +resulting HTML is output.

+

Otherwise, format() calls self.wrap(), which wraps the generator with +other generators. These may add some HTML code to the one generated by +_format_lines(), either by modifying the lines generated by the latter, +then yielding them again with (1, line), and/or by yielding other HTML +code before or after the lines, with (0, html). The distinction between +source lines and other code makes it possible to wrap the generator multiple +times.

+

The default wrap() implementation adds a <div> and a <pre> tag.

+

A custom HtmlFormatter subclass could look like this:

+
class CodeHtmlFormatter(HtmlFormatter):
+
+    def wrap(self, source, outfile):
+        return self._wrap_code(source)
+
+    def _wrap_code(self, source):
+        yield 0, '<code>'
+        for i, t in source:
+            if i == 1:
+                # it's a line of formatted code
+                t += '<br>'
+            yield i, t
+        yield 0, '</code>'
+
+

This results in wrapping the formatted lines with a <code> tag, where the +source lines are broken using <br> tags.

+

After calling wrap(), the format() method also adds the "line numbers" +and/or "full document" wrappers if the respective options are set. Then, all +HTML yielded by the wrapped generator is output.

+ +++ + + + + + +
Short names:html
Filename patterns:*.html, *.htm
+
+
+
+

ImageFormatter

+
+

Create a PNG image from source code. This uses the Python Imaging Library to +generate a pixmap from the source code.

+

New in Pygments 0.10.

+

Additional options accepted:

+
+
image_format
+

An image format to output to that is recognised by PIL, these include:

+
    +
  • "PNG" (default)
  • +
  • "JPEG"
  • +
  • "BMP"
  • +
  • "GIF"
  • +
+
+
line_pad
+

The extra spacing (in pixels) between each line of text.

+

Default: 2

+
+
font_name
+

The font name to be used as the base font from which others, such as +bold and italic fonts will be generated. This really should be a +monospace font to look sane.

+

Default: "Bitstream Vera Sans Mono"

+
+
font_size
+

The font size in points to be used.

+

Default: 14

+
+
image_pad
+

The padding, in pixels to be used at each edge of the resulting image.

+

Default: 10

+
+
line_numbers
+

Whether line numbers should be shown: True/False

+

Default: True

+
+
line_number_step
+

The step used when printing line numbers.

+

Default: 1

+
+
line_number_bg
+

The background colour (in "#123456" format) of the line number bar, or +None to use the style background color.

+

Default: "#eed"

+
+
line_number_fg
+

The text color of the line numbers (in "#123456"-like format).

+

Default: "#886"

+
+
line_number_chars
+

The number of columns of line numbers allowable in the line number +margin.

+

Default: 2

+
+
line_number_bold
+

Whether line numbers will be bold: True/False

+

Default: False

+
+
line_number_italic
+

Whether line numbers will be italicized: True/False

+

Default: False

+
+
line_number_separator
+

Whether a line will be drawn between the line number area and the +source code area: True/False

+

Default: True

+
+
line_number_pad
+

The horizontal padding (in pixels) between the line number margin, and +the source code area.

+

Default: 6

+
+
+ +++ + + + + + +
Short names:img, IMG, png
Filename patterns:*.png
+
+
+
+

JpgImageFormatter

+
+

Create a JPEG image from source code. This uses the Python Imaging Library to +generate a pixmap from the source code.

+

New in Pygments 1.0. (You could create JPEG images before by passing a +suitable image_format option to the ImageFormatter.)

+ +++ + + + + + +
Short names:jpg, jpeg
Filename patterns:*.jpg
+
+
+
+

LatexFormatter

+
+

Format tokens as LaTeX code. This needs the fancyvrb and color +standard packages.

+

Without the full option, code is formatted as one Verbatim +environment, like this:

+
\begin{Verbatim}[commandchars=@\[\]]
+@PY[k][def ]@PY[n+nf][foo](@PY[n][bar]):
+    @PY[k][pass]
+\end{Verbatim}
+
+

The special command used here (@PY) and all the other macros it needs +are output by the get_style_defs method.

+

With the full option, a complete LaTeX document is output, including +the command definitions in the preamble.

+

The get_style_defs() method of a LatexFormatter returns a string +containing \def commands defining the macros needed inside the +Verbatim environments.

+

Additional options accepted:

+
+
style
+
The style to use, can be a string or a Style subclass (default: +'default').
+
full
+
Tells the formatter to output a "full" document, i.e. a complete +self-contained document (default: False).
+
title
+
If full is true, the title that should be used to caption the +document (default: '').
+
docclass
+
If the full option is enabled, this is the document class to use +(default: 'article').
+
preamble
+
If the full option is enabled, this can be further preamble commands, +e.g. \usepackage (default: '').
+
linenos
+
If set to True, output line numbers (default: False).
+
linenostart
+
The line number for the first line (default: 1).
+
linenostep
+
If set to a number n > 1, only every nth line number is printed.
+
verboptions
+
Additional options given to the Verbatim environment (see the fancyvrb +docs for possible values) (default: '').
+
commandprefix
+

The LaTeX commands used to produce colored output are constructed +using this prefix and some letters (default: 'PY'). +New in Pygments 0.7.

+

New in Pygments 0.10: the default is now 'PY' instead of 'C'.

+
+
+ +++ + + + + + +
Short names:latex, tex
Filename patterns:*.tex
+
+
+
+

NullFormatter

+
+

Output the text unchanged without any formatting.

+ +++ + + + + + +
Short names:text, null
Filename patterns:*.txt
+
+
+
+

RawTokenFormatter

+
+

Format tokens as a raw representation for storing token streams.

+

The format is tokentype<TAB>repr(tokenstring)\n. The output can later +be converted to a token stream with the RawTokenLexer, described in the +lexer list.

+

Only two options are accepted:

+
+
compress
+
If set to 'gz' or 'bz2', compress the output with the given +compression algorithm after encoding (default: '').
+
error_color
+
If set to a color name, highlight error tokens using that color. If +set but with no value, defaults to 'red'. +New in Pygments 0.11.
+
+ +++ + + + + + +
Short names:raw, tokens
Filename patterns:*.raw
+
+
+
+

RtfFormatter

+
+

Format tokens as RTF markup. This formatter automatically outputs full RTF +documents with color information and other useful stuff. Perfect for Copy and +Paste into Microsoft® Word® documents.

+

New in Pygments 0.6.

+

Additional options accepted:

+
+
style
+
The style to use, can be a string or a Style subclass (default: +'default').
+
fontface
+
The used font famliy, for example Bitstream Vera Sans. Defaults to +some generic font which is supposed to have fixed width.
+
+ +++ + + + + + +
Short names:rtf
Filename patterns:*.rtf
+
+
+
+

SvgFormatter

+
+

Format tokens as an SVG graphics file. This formatter is still experimental. +Each line of code is a <text> element with explicit x and y +coordinates containing <tspan> elements with the individual token styles.

+

By default, this formatter outputs a full SVG document including doctype +declaration and the <svg> root element.

+

New in Pygments 0.9.

+

Additional options accepted:

+
+
nowrap
+
Don't wrap the SVG <text> elements in <svg><g> elements and +don't add a XML declaration and a doctype. If true, the fontfamily +and fontsize options are ignored. Defaults to False.
+
fontfamily
+
The value to give the wrapping <g> element's font-family +attribute, defaults to "monospace".
+
fontsize
+
The value to give the wrapping <g> element's font-size +attribute, defaults to "14px".
+
xoffset
+
Starting offset in X direction, defaults to 0.
+
yoffset
+
Starting offset in Y direction, defaults to the font size if it is given +in pixels, or 20 else. (This is necessary since text coordinates +refer to the text baseline, not the top edge.)
+
ystep
+
Offset to add to the Y coordinate for each subsequent line. This should +roughly be the text size plus 5. It defaults to that value if the text +size is given in pixels, or 25 else.
+
spacehack
+
Convert spaces in the source to &#160;, which are non-breaking +spaces. SVG provides the xml:space attribute to control how +whitespace inside tags is handled, in theory, the preserve value +could be used to keep all whitespace as-is. However, many current SVG +viewers don't obey that rule, so this option is provided as a workaround +and defaults to True.
+
+ +++ + + + + + +
Short names:svg
Filename patterns:*.svg
+
+
+
+

Terminal256Formatter

+
+

Format tokens with ANSI color sequences, for output in a 256-color +terminal or console. Like in TerminalFormatter color sequences +are terminated at newlines, so that paging the output works correctly.

+

The formatter takes colors from a style defined by the style option +and converts them to nearest ANSI 256-color escape sequences. Bold and +underline attributes from the style are preserved (and displayed).

+

New in Pygments 0.9.

+

Options accepted:

+
+
style
+
The style to use, can be a string or a Style subclass (default: +'default').
+
+ +++ + + + + + +
Short names:terminal256, console256, 256
Filename patterns:None
+
+
+
+

TerminalFormatter

+
+

Format tokens with ANSI color sequences, for output in a text console. +Color sequences are terminated at newlines, so that paging the output +works correctly.

+

The get_style_defs() method doesn't do anything special since there is +no support for common styles.

+

Options accepted:

+
+
bg
+
Set to "light" or "dark" depending on the terminal's background +(default: "light").
+
colorscheme
+
A dictionary mapping token types to (lightbg, darkbg) color names or +None (default: None = use builtin colorscheme).
+
+ +++ + + + + + +
Short names:terminal, console
Filename patterns:None
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/index.html b/docs/build/index.html new file mode 100644 index 0000000..4e5a647 --- /dev/null +++ b/docs/build/index.html @@ -0,0 +1,261 @@ + + + + Overview — Pygments + + + + +
+

Pygments

+

Overview

+ + + +

Welcome to the Pygments documentation.

+ +
+

If you find bugs or have suggestions for the documentation, please +look here for info on how to contact the team.

+

You can download an offline version of this documentation from the +download page.

+ +
+ + + \ No newline at end of file diff --git a/docs/build/installation.html b/docs/build/installation.html new file mode 100644 index 0000000..2320d5c --- /dev/null +++ b/docs/build/installation.html @@ -0,0 +1,281 @@ + + + + Installation — Pygments + + + + +
+

Pygments

+

Installation

+ + « Back To Index + + + + + +

Pygments requires at least Python 2.3 to work correctly. Just to clarify: +there won't ever be support for Python versions below 2.3. However, there +are no other dependencies.

+
+

Installing a released version

+
+

As a Python egg (via easy_install)

+

You can install the most recent Pygments version using easy_install:

+
+sudo easy_install Pygments
+
+

This will install a Pygments egg in your Python installation's site-packages +directory.

+
+
+

From the tarball release

+
    +
  1. Download the most recent tarball from the download page
  2. +
  3. Unpack the tarball
  4. +
  5. sudo python setup.py install
  6. +
+

Note that the last command will automatically download and install +setuptools if you don't already have it installed. This requires a working +internet connection.

+

This will install Pygments into your Python installation's site-packages directory.

+
+
+
+

Installing the development version

+
+

If you want to play around with the code

+
    +
  1. Install Mercurial
  2. +
  3. hg clone http://dev.pocoo.org/hg/pygments-main pygments
  4. +
  5. cd pygments
  6. +
  7. ln -s pygments /usr/lib/python2.X/site-packages
  8. +
  9. ln -s pygmentize /usr/local/bin
  10. +
+

As an alternative to steps 4 and 5 you can also do python setup.py develop +which will install the package via setuptools in development mode.

+ +
+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/integrate.html b/docs/build/integrate.html new file mode 100644 index 0000000..5ae2a3d --- /dev/null +++ b/docs/build/integrate.html @@ -0,0 +1,255 @@ + + + + Using Pygments in various scenarios — Pygments + + + + +
+

Pygments

+

Using Pygments in various scenarios

+ + « Back To Index + + +
+

Contents

+ +
+ + +
+

PyGtk

+

Armin has written a piece of sample code that shows how to create a Gtk +TextBuffer object containing Pygments-highlighted text.

+

See the article here: http://lucumr.pocoo.org/cogitations/2007/05/30/pygments-gtk-rendering/

+
+
+

Wordpress

+

He also has a snippet that shows how to use Pygments in WordPress:

+

http://lucumr.pocoo.org/cogitations/2007/05/30/pygments-in-wordpress/

+
+
+

Markdown

+

Since Pygments 0.9, the distribution ships Markdown preprocessor sample code +that uses Pygments to render source code in external/markdown-processor.py. +You can copy and adapt it to your liking.

+
+
+

TextMate

+

Antonio Cangiano has created a Pygments bundle for TextMate that allows to +colorize code via a simple menu option. It can be found here.

+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/lexerdevelopment.html b/docs/build/lexerdevelopment.html new file mode 100644 index 0000000..a802c3c --- /dev/null +++ b/docs/build/lexerdevelopment.html @@ -0,0 +1,691 @@ + + + + Write your own lexer — Pygments + + + + +
+

Pygments

+

Write your own lexer

+ + « Back To Index + + + + + +

If a lexer for your favorite language is missing in the Pygments package, you can +easily write your own and extend Pygments.

+

All you need can be found inside the pygments.lexer module. As you can read in +the API documentation, a lexer is a class that is initialized with +some keyword arguments (the lexer options) and that provides a +get_tokens_unprocessed() method which is given a string or unicode object with +the data to parse.

+

The get_tokens_unprocessed() method must return an iterator or iterable +containing tuples in the form (index, token, value). Normally you don't need +to do this since there are numerous base lexers you can subclass.

+
+

RegexLexer

+

A very powerful (but quite easy to use) lexer is the RegexLexer. This lexer +base class allows you to define lexing rules in terms of regular expressions +for different states.

+

States are groups of regular expressions that are matched against the input +string at the current position. If one of these expressions matches, a +corresponding action is performed (normally yielding a token with a specific +type), the current position is set to where the last match ended and the +matching process continues with the first regex of the current state.

+

Lexer states are kept in a state stack: each time a new state is entered, the +new state is pushed onto the stack. The most basic lexers (like the +DiffLexer) just need one state.

+

Each state is defined as a list of tuples in the form (regex, action, +new_state) where the last item is optional. In the most basic form, action +is a token type (like Name.Builtin). That means: When regex matches, emit a +token with the match text and type tokentype and push new_state on the state +stack. If the new state is '#pop', the topmost state is popped from the +stack instead. (To pop more than one state, use '#pop:2' and so on.) +'#push' is a synonym for pushing the current state on the +stack.

+

The following example shows the DiffLexer from the builtin lexers. Note that +it contains some additional attributes name, aliases and filenames which +aren't required for a lexer. They are used by the builtin lexer lookup +functions.

+
from pygments.lexer import RegexLexer
+from pygments.token import *
+
+class DiffLexer(RegexLexer):
+    name = 'Diff'
+    aliases = ['diff']
+    filenames = ['*.diff']
+
+    tokens = {
+        'root': [
+            (r' .*\n', Text),
+            (r'\+.*\n', Generic.Inserted),
+            (r'-.*\n', Generic.Deleted),
+            (r'@.*\n', Generic.Subheading),
+            (r'Index.*\n', Generic.Heading),
+            (r'=.*\n', Generic.Heading),
+            (r'.*\n', Text),
+        ]
+    }
+
+

As you can see this lexer only uses one state. When the lexer starts scanning +the text, it first checks if the current character is a space. If this is true +it scans everything until newline and returns the parsed data as Text token.

+

If this rule doesn't match, it checks if the current char is a plus sign. And +so on.

+

If no rule matches at the current position, the current char is emitted as an +Error token that indicates a parsing error, and the position is increased by +1.

+
+
+

Regex Flags

+

You can either define regex flags in the regex (r'(?x)foo bar') or by adding +a flags attribute to your lexer class. If no attribute is defined, it defaults +to re.MULTILINE. For more informations about regular expression flags see the +regular expressions help page in the python documentation.

+
+
+

Scanning multiple tokens at once

+

Here is a more complex lexer that highlights INI files. INI files consist of +sections, comments and key = value pairs:

+
from pygments.lexer import RegexLexer, bygroups
+from pygments.token import *
+
+class IniLexer(RegexLexer):
+    name = 'INI'
+    aliases = ['ini', 'cfg']
+    filenames = ['*.ini', '*.cfg']
+
+    tokens = {
+        'root': [
+            (r'\s+', Text),
+            (r';.*?$', Comment),
+            (r'\[.*?\]$', Keyword),
+            (r'(.*?)(\s*)(=)(\s*)(.*?)$',
+             bygroups(Name.Attribute, Text, Operator, Text, String))
+        ]
+    }
+
+

The lexer first looks for whitespace, comments and section names. And later it +looks for a line that looks like a key, value pair, seperated by an '=' +sign, and optional whitespace.

+

The bygroups helper makes sure that each group is yielded with a different +token type. First the Name.Attribute token, then a Text token for the +optional whitespace, after that a Operator token for the equals sign. Then a +Text token for the whitespace again. The rest of the line is returned as +String.

+

Note that for this to work, every part of the match must be inside a capturing +group (a (...)), and there must not be any nested capturing groups. If you +nevertheless need a group, use a non-capturing group defined using this syntax: +r'(?:some|words|here)' (note the ?: after the beginning parenthesis).

+

If you find yourself needing a capturing group inside the regex which +shouldn't be part of the output but is used in the regular expressions for +backreferencing (eg: r'(<(foo|bar)>)(.*?)(</\2>)'), you can pass None +to the bygroups function and it will skip that group will be skipped in the +output.

+
+
+

Changing states

+

Many lexers need multiple states to work as expected. For example, some +languages allow multiline comments to be nested. Since this is a recursive +pattern it's impossible to lex just using regular expressions.

+

Here is the solution:

+
from pygments.lexer import RegexLexer
+from pygments.token import *
+
+class ExampleLexer(RegexLexer):
+    name = 'Example Lexer with states'
+
+    tokens = {
+        'root': [
+            (r'[^/]+', Text),
+            (r'/\*', Comment.Multiline, 'comment'),
+            (r'//.*?$', Comment.Singleline),
+            (r'/', Text)
+        ],
+        'comment': [
+            (r'[^*/]', Comment.Multiline),
+            (r'/\*', Comment.Multiline, '#push'),
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'[*/]', Comment.Multiline)
+        ]
+    }
+
+

This lexer starts lexing in the 'root' state. It tries to match as much as +possible until it finds a slash ('/'). If the next character after the slash +is a star ('*') the RegexLexer sends those two characters to the output +stream marked as Comment.Multiline and continues parsing with the rules +defined in the 'comment' state.

+

If there wasn't a star after the slash, the RegexLexer checks if it's a +singleline comment (eg: followed by a second slash). If this also wasn't the +case it must be a single slash (the separate regex for a single slash must also +be given, else the slash would be marked as an error token).

+

Inside the 'comment' state, we do the same thing again. Scan until the lexer +finds a star or slash. If it's the opening of a multiline comment, push the +'comment' state on the stack and continue scanning, again in the +'comment' state. Else, check if it's the end of the multiline comment. If +yes, pop one state from the stack.

+

Note: If you pop from an empty stack you'll get an IndexError. (There is an +easy way to prevent this from happening: don't '#pop' in the root state).

+

If the RegexLexer encounters a newline that is flagged as an error token, the +stack is emptied and the lexer continues scanning in the 'root' state. This +helps producing error-tolerant highlighting for erroneous input, e.g. when a +single-line string is not closed.

+
+
+

Advanced state tricks

+

There are a few more things you can do with states:

+
    +
  • You can push multiple states onto the stack if you give a tuple instead of a +simple string as the third item in a rule tuple. For example, if you want to +match a comment containing a directive, something like:

    +
    +/* <processing directive>    rest of comment */
    +
    +

    you can use this rule:

    +
    tokens = {
    +    'root': [
    +        (r'/\* <', Comment, ('comment', 'directive')),
    +        ...
    +    ],
    +    'directive': [
    +        (r'[^>]*', Comment.Directive),
    +        (r'>', Comment, '#pop'),
    +    ],
    +    'comment': [
    +        (r'[^*]+', Comment),
    +        (r'\*/', Comment, '#pop'),
    +        (r'\*', Comment),
    +    ]
    +}
    +
    +

    When this encounters the above sample, first 'comment' and 'directive' +are pushed onto the stack, then the lexer continues in the directive state +until it finds the closing >, then it continues in the comment state until +the closing */. Then, both states are popped from the stack again and +lexing continues in the root state.

    +

    New in Pygments 0.9: The tuple can contain the special '#push' and +'#pop' (but not '#pop:n') directives.

    +
  • +
  • You can include the rules of a state in the definition of another. This is +done by using include from pygments.lexer:

    +
    from pygments.lexer import RegexLexer, bygroups, include
    +from pygments.token import *
    +
    +class ExampleLexer(RegexLexer):
    +    tokens = {
    +        'comments': [
    +            (r'/\*.*?\*/', Comment),
    +            (r'//.*?\n', Comment),
    +        ],
    +        'root': [
    +            include('comments'),
    +            (r'(function )(\w+)( {)',
    +             bygroups(Keyword, Name, Keyword), 'function'),
    +            (r'.', Text),
    +        ],
    +        'function': [
    +            (r'[^}/]+', Text),
    +            include('comments'),
    +            (r'/', Text),
    +            (r'}', Keyword, '#pop'),
    +        ]
    +    }
    +
    +

    This is a hypothetical lexer for a language that consist of functions and +comments. Because comments can occur at toplevel and in functions, we need +rules for comments in both states. As you can see, the include helper saves +repeating rules that occur more than once (in this example, the state +'comment' will never be entered by the lexer, as it's only there to be +included in 'root' and 'function').

    +
  • +
  • Sometimes, you may want to "combine" a state from existing ones. This is +possible with the combine helper from pygments.lexer.

    +

    If you, instead of a new state, write combined('state1', 'state2') as the +third item of a rule tuple, a new anonymous state will be formed from state1 +and state2 and if the rule matches, the lexer will enter this state.

    +

    This is not used very often, but can be helpful in some cases, such as the +PythonLexer's string literal processing.

    +
  • +
  • If you want your lexer to start lexing in a different state you can modify +the stack by overloading the get_tokens_unprocessed() method:

    +
    from pygments.lexer import RegexLexer
    +
    +class MyLexer(RegexLexer):
    +    tokens = {...}
    +
    +    def get_tokens_unprocessed(self, text):
    +        stack = ['root', 'otherstate']
    +        for item in RegexLexer.get_tokens_unprocessed(text, stack):
    +            yield item
    +
    +

    Some lexers like the PhpLexer use this to make the leading <?php +preprocessor comments optional. Note that you can crash the lexer easily +by putting values into the stack that don't exist in the token map. Also +removing 'root' from the stack can result in strange errors!

    +
  • +
  • An empty regex at the end of a state list, combined with '#pop', can +act as a return point from a state that doesn't have a clear end marker.

    +
  • +
+
+
+

Using multiple lexers

+

Using multiple lexers for the same input can be tricky. One of the easiest +combination techniques is shown here: You can replace the token type entry in a +rule tuple (the second item) with a lexer class. The matched text will then be +lexed with that lexer, and the resulting tokens will be yielded.

+

For example, look at this stripped-down HTML lexer:

+
from pygments.lexer import RegexLexer, bygroups, using
+from pygments.token import *
+from pygments.lexers.web import JavascriptLexer
+
+class HtmlLexer(RegexLexer):
+    name = 'HTML'
+    aliases = ['html']
+    filenames = ['*.html', '*.htm']
+
+    flags = re.IGNORECASE | re.DOTALL
+    tokens = {
+        'root': [
+            ('[^<&]+', Text),
+            ('&.*?;', Name.Entity),
+            (r'<\s*script\s*', Name.Tag, ('script-content', 'tag')),
+            (r'<\s*[a-zA-Z0-9:]+', Name.Tag, 'tag'),
+            (r'<\s*/\s*[a-zA-Z0-9:]+\s*>', Name.Tag),
+        ],
+        'script-content': [
+            (r'(.+?)(<\s*/\s*script\s*>)',
+             bygroups(using(JavascriptLexer), Name.Tag),
+             '#pop'),
+        ]
+    }
+
+

Here the content of a <script> tag is passed to a newly created instance of +a JavascriptLexer and not processed by the HtmlLexer. This is done using the +using helper that takes the other lexer class as its parameter.

+

Note the combination of bygroups and using. This makes sure that the content +up to the </script> end tag is processed by the JavascriptLexer, while the +end tag is yielded as a normal token with the Name.Tag type.

+

As an additional goodie, if the lexer class is replaced by this (imported from +pygments.lexer), the "other" lexer will be the current one (because you cannot +refer to the current class within the code that runs at class definition time).

+

Also note the (r'<\s*script\s*', Name.Tag, ('script-content', 'tag')) rule. +Here, two states are pushed onto the state stack, 'script-content' and +'tag'. That means that first 'tag' is processed, which will parse +attributes and the closing >, then the 'tag' state is popped and the +next state on top of the stack will be 'script-content'.

+

The using() helper has a special keyword argument, state, which works as +follows: if given, the lexer to use initially is not in the "root" state, +but in the state given by this argument. This only works with a RegexLexer.

+

Any other keywords arguments passed to using() are added to the keyword +arguments used to create the lexer.

+
+
+

Delegating Lexer

+

Another approach for nested lexers is the DelegatingLexer which is for +example used for the template engine lexers. It takes two lexers as +arguments on initialisation: a root_lexer and a language_lexer.

+

The input is processed as follows: First, the whole text is lexed with the +language_lexer. All tokens yielded with a type of Other are then +concatenated and given to the root_lexer. The language tokens of the +language_lexer are then inserted into the root_lexer's token stream +at the appropriate positions.

+
from pygments.lexer import DelegatingLexer
+from pygments.lexers.web import HtmlLexer, PhpLexer
+
+class HtmlPhpLexer(DelegatingLexer):
+    def __init__(self, **options):
+        super(HtmlPhpLexer, self).__init__(HtmlLexer, PhpLexer, **options)
+
+

This procedure ensures that e.g. HTML with template tags in it is highlighted +correctly even if the template tags are put into HTML tags or attributes.

+

If you want to change the needle token Other to something else, you can +give the lexer another token type as the third parameter:

+
DelegatingLexer.__init__(MyLexer, OtherLexer, Text, **options)
+
+
+
+

Callbacks

+

Sometimes the grammar of a language is so complex that a lexer would be unable +to parse it just by using regular expressions and stacks.

+

For this, the RegexLexer allows callbacks to be given in rule tuples, instead +of token types (bygroups and using are nothing else but preimplemented +callbacks). The callback must be a function taking two arguments:

+
    +
  • the lexer itself
  • +
  • the match object for the last matched rule
  • +
+

The callback must then return an iterable of (or simply yield) (index, +tokentype, value) tuples, which are then just passed through by +get_tokens_unprocessed(). The index here is the position of the token in +the input string, tokentype is the normal token type (like Name.Builtin), +and value the associated part of the input string.

+

You can see an example here:

+
from pygments.lexer import RegexLexer
+from pygments.token import Generic
+
+class HypotheticLexer(RegexLexer):
+
+    def headline_callback(lexer, match):
+        equal_signs = match.group(1)
+        text = match.group(2)
+        yield match.start(), Generic.Headline, equal_signs + text + equal_signs
+
+    tokens = {
+        'root': [
+            (r'(=+)(.*?)(\1)', headline_callback)
+        ]
+    }
+
+

If the regex for the headline_callback matches, the function is called with the +match object. Note that after the callback is done, processing continues +normally, that is, after the end of the previous match. The callback has no +possibility to influence the position.

+

There are not really any simple examples for lexer callbacks, but you can see +them in action e.g. in the compiled.py source code in the CLexer and +JavaLexer classes.

+
+
+

The ExtendedRegexLexer class

+

The RegexLexer, even with callbacks, unfortunately isn't powerful enough for +the funky syntax rules of some languages that will go unnamed, such as Ruby.

+

But fear not; even then you don't have to abandon the regular expression +approach. For Pygments has a subclass of RegexLexer, the ExtendedRegexLexer. +All features known from RegexLexers are available here too, and the tokens are +specified in exactly the same way, except for one detail:

+

The get_tokens_unprocessed() method holds its internal state data not as local +variables, but in an instance of the pygments.lexer.LexerContext class, and +that instance is passed to callbacks as a third argument. This means that you +can modify the lexer state in callbacks.

+

The LexerContext class has the following members:

+
    +
  • text -- the input text
  • +
  • pos -- the current starting position that is used for matching regexes
  • +
  • stack -- a list containing the state stack
  • +
  • end -- the maximum position to which regexes are matched, this defaults to +the length of text
  • +
+

Additionally, the get_tokens_unprocessed() method can be given a +LexerContext instead of a string and will then process this context instead of +creating a new one for the string argument.

+

Note that because you can set the current position to anything in the callback, +it won't be automatically be set by the caller after the callback is finished. +For example, this is how the hypothetical lexer above would be written with the +ExtendedRegexLexer:

+
from pygments.lexer import ExtendedRegexLexer
+from pygments.token import Generic
+
+class ExHypotheticLexer(ExtendedRegexLexer):
+
+    def headline_callback(lexer, match, ctx):
+        equal_signs = match.group(1)
+        text = match.group(2)
+        yield match.start(), Generic.Headline, equal_signs + text + equal_signs
+        ctx.pos = match.end()
+
+    tokens = {
+        'root': [
+            (r'(=+)(.*?)(\1)', headline_callback)
+        ]
+    }
+
+

This might sound confusing (and it can really be). But it is needed, and for an +example look at the Ruby lexer in agile.py.

+
+
+

Filtering Token Streams

+

Some languages ship a lot of builtin functions (for example PHP). The total +amount of those functions differs from system to system because not everybody +has every extension installed. In the case of PHP there are over 3000 builtin +functions. That's an incredible huge amount of functions, much more than you +can put into a regular expression.

+

But because only Name tokens can be function names it's solvable by overriding +the get_tokens_unprocessed() method. The following lexer subclasses the +PythonLexer so that it highlights some additional names as pseudo keywords:

+
from pygments.lexers.agile import PythonLexer
+from pygments.token import Name, Keyword
+
+class MyPythonLexer(PythonLexer):
+    EXTRA_KEYWORDS = ['foo', 'bar', 'foobar', 'barfoo', 'spam', 'eggs']
+
+    def get_tokens_unprocessed(self, text):
+        for index, token, value in PythonLexer.get_tokens_unprocessed(self, text):
+            if token is Name and value in self.EXTRA_KEYWORDS:
+                yield index, Keyword.Pseudo, value
+            else:
+                yield index, token, value
+
+

The PhpLexer and LuaLexer use this method to resolve builtin functions.

+

Note Do not confuse this with the filter system.

+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/lexers.html b/docs/build/lexers.html new file mode 100644 index 0000000..893cfac --- /dev/null +++ b/docs/build/lexers.html @@ -0,0 +1,3175 @@ + + + + Available lexers — Pygments + + + + +
+

Pygments

+

Available lexers

+ + « Back To Index + + + + + +

This page lists all available builtin lexers and the options they take.

+

Currently, all lexers support these options:

+
+
stripnl
+
Strip leading and trailing newlines from the input (default: True)
+
stripall
+
Strip all leading and trailing whitespace from the input (default: +False).
+
tabsize
+
If given and greater than 0, expand tabs in the input (default: 0).
+
encoding
+

New in Pygments 0.6.

+

If given, must be an encoding name (such as "utf-8"). This encoding +will be used to convert the input string to Unicode (if it is not already +a Unicode string). The default is "latin1".

+

If this option is set to "guess", a simple UTF-8 vs. Latin-1 +detection is used, if it is set to "chardet", the +chardet library is used to +guess the encoding of the input.

+
+
+

The "Short Names" field lists the identifiers that can be used with the +get_lexer_by_name() function.

+

These lexers are builtin and can be imported from pygments.lexers:

+
+

Lexers for agile languages

+

ClojureLexer

+
+

Lexer for Clojure source code.

+

New in Pygments 0.11.

+ +++ + + + + + + + +
Short names:clojure, clj
Filename patterns:*.clj
Mimetypes:text/x-clojure, application/x-clojure
+
+

IoLexer

+
+

For Io (a small, prototype-based +programming language) source.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:io
Filename patterns:*.io
Mimetypes:text/x-iosrc
+
+

LuaLexer

+
+

For Lua source code.

+

Additional options accepted:

+
+
func_name_highlighting
+
If given and True, highlight builtin function names +(default: True).
+
disabled_modules
+

If given, must be a list of module names whose function names +should not be highlighted. By default all modules are highlighted.

+

To get a list of allowed modules have a look into the +_luabuiltins module:

+
>>> from pygments.lexers._luabuiltins import MODULES
+>>> MODULES.keys()
+['string', 'coroutine', 'modules', 'io', 'basic', ...]
+
+
+
+ +++ + + + + + + + +
Short names:lua
Filename patterns:*.lua
Mimetypes:text/x-lua, application/x-lua
+
+

MiniDLexer

+
+

For MiniD (a D-like scripting +language) source.

+ +++ + + + + + + + +
Short names:minid
Filename patterns:*.md
Mimetypes:text/x-minidsrc
+
+

PerlLexer

+
+

For Perl source code.

+ +++ + + + + + + + +
Short names:perl, pl
Filename patterns:*.pl, *.pm
Mimetypes:text/x-perl, application/x-perl
+
+

Python3Lexer

+
+

For Python source code (version 3.0).

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:python3, py3
Filename patterns:None
Mimetypes:text/x-python3, application/x-python3
+
+

Python3TracebackLexer

+
+

For Python 3.0 tracebacks, with support for chained exceptions.

+

New in Pygments 1.0.

+ +++ + + + + + + + +
Short names:py3tb
Filename patterns:*.py3tb
Mimetypes:text/x-python3-traceback
+
+

PythonConsoleLexer

+
+

For Python console output or doctests, such as:

+
>>> a = 'foo'
+>>> print a
+foo
+>>> 1 / 0
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+ZeroDivisionError: integer division or modulo by zero
+
+

Additional options:

+
+
python3
+
Use Python 3 lexer for code. Default is False. +New in Pygments 1.0.
+
+ +++ + + + + + + + +
Short names:pycon
Filename patterns:None
Mimetypes:text/x-python-doctest
+
+

PythonLexer

+
+

For Python source code.

+ +++ + + + + + + + +
Short names:python, py
Filename patterns:*.py, *.pyw, *.sc, SConstruct, SConscript
Mimetypes:text/x-python, application/x-python
+
+

PythonTracebackLexer

+
+

For Python tracebacks.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:pytb
Filename patterns:*.pytb
Mimetypes:text/x-python-traceback
+
+

RubyConsoleLexer

+
+

For Ruby interactive console (irb) output like:

+
irb(main):001:0> a = 1
+=> 1
+irb(main):002:0> puts a
+1
+=> nil
+
+ +++ + + + + + + + +
Short names:rbcon, irb
Filename patterns:None
Mimetypes:text/x-ruby-shellsession
+
+

RubyLexer

+
+

For Ruby source code.

+ +++ + + + + + + + +
Short names:rb, ruby
Filename patterns:*.rb, *.rbw, Rakefile, *.rake, *.gemspec, *.rbx
Mimetypes:text/x-ruby, application/x-ruby
+
+

TclLexer

+
+

For Tcl source code.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:tcl
Filename patterns:*.tcl
Mimetypes:text/x-tcl, text/x-script.tcl, application/x-tcl
+
+
+
+

Lexers for assembly languages

+

CObjdumpLexer

+
+

For the output of 'objdump -Sr on compiled C files'

+ +++ + + + + + + + +
Short names:c-objdump
Filename patterns:*.c-objdump
Mimetypes:text/x-c-objdump
+
+

CppObjdumpLexer

+
+

For the output of 'objdump -Sr on compiled C++ files'

+ +++ + + + + + + + +
Short names:cpp-objdump, c++-objdumb, cxx-objdump
Filename patterns:*.cpp-objdump, *.c++-objdump, *.cxx-objdump
Mimetypes:text/x-cpp-objdump
+
+

DObjdumpLexer

+
+

For the output of 'objdump -Sr on compiled D files'

+ +++ + + + + + + + +
Short names:d-objdump
Filename patterns:*.d-objdump
Mimetypes:text/x-d-objdump
+
+

GasLexer

+
+

For Gas (AT&T) assembly code.

+ +++ + + + + + + + +
Short names:gas
Filename patterns:*.s, *.S
Mimetypes:text/x-gas
+
+

LlvmLexer

+
+

For LLVM assembly code.

+ +++ + + + + + + + +
Short names:llvm
Filename patterns:*.ll
Mimetypes:text/x-llvm
+
+

NasmLexer

+
+

For Nasm (Intel) assembly code.

+ +++ + + + + + + + +
Short names:nasm
Filename patterns:*.asm, *.ASM
Mimetypes:text/x-nasm
+
+

ObjdumpLexer

+
+

For the output of 'objdump -dr'

+ +++ + + + + + + + +
Short names:objdump
Filename patterns:*.objdump
Mimetypes:text/x-objdump
+
+
+
+

Lexers for compiled languages

+

CLexer

+
+

For C source code with preprocessor directives.

+ +++ + + + + + + + +
Short names:c
Filename patterns:*.c, *.h
Mimetypes:text/x-chdr, text/x-csrc
+
+

CppLexer

+
+

For C++ source code with preprocessor directives.

+ +++ + + + + + + + +
Short names:cpp, c++
Filename patterns:*.cpp, *.hpp, *.c++, *.h++, *.cc, *.hh, *.cxx, *.hxx
Mimetypes:text/x-c++hdr, text/x-c++src
+
+

CythonLexer

+
+

For Pyrex and Cython source code.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:cython, pyx
Filename patterns:*.pyx, *.pxd, *.pxi
Mimetypes:text/x-cython, application/x-cython
+
+

DLexer

+
+

For D source.

+ +++ + + + + + + + +
Short names:d
Filename patterns:*.d, *.di
Mimetypes:text/x-dsrc
+
+

DelphiLexer

+
+

For Delphi (Borland Object Pascal), +Turbo Pascal and Free Pascal source code.

+

Additional options accepted:

+
+
turbopascal
+
Highlight Turbo Pascal specific keywords (default: True).
+
delphi
+
Highlight Borland Delphi specific keywords (default: True).
+
freepascal
+
Highlight Free Pascal specific keywords (default: True).
+
units
+
A list of units that should be considered builtin, supported are +System, SysUtils, Classes and Math. +Default is to consider all of them builtin.
+
+ +++ + + + + + + + +
Short names:delphi, pas, pascal, objectpascal
Filename patterns:*.pas
Mimetypes:text/x-pascal
+
+

DylanLexer

+
+

For the Dylan language.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:dylan
Filename patterns:*.dylan
Mimetypes:text/x-dylan
+
+

FortranLexer

+
+

Lexer for FORTRAN 90 code.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:fortran
Filename patterns:*.f, *.f90
Mimetypes:text/x-fortran
+
+

GLShaderLexer

+
+

GLSL (OpenGL Shader) lexer.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:glsl
Filename patterns:*.vert, *.frag, *.geo
Mimetypes:text/x-glslsrc
+
+

JavaLexer

+
+

For Java source code.

+ +++ + + + + + + + +
Short names:java
Filename patterns:*.java
Mimetypes:text/x-java
+
+

ObjectiveCLexer

+
+

For Objective-C source code with preprocessor directives.

+ +++ + + + + + + + +
Short names:objective-c, objectivec, obj-c, objc
Filename patterns:*.m
Mimetypes:text/x-objective-c
+
+

PrologLexer

+
+

Lexer for Prolog files.

+ +++ + + + + + + + +
Short names:prolog
Filename patterns:*.prolog, *.pro, *.pl
Mimetypes:text/x-prolog
+
+

ScalaLexer

+
+

For Scala source code.

+ +++ + + + + + + + +
Short names:scala
Filename patterns:*.scala
Mimetypes:text/x-scala
+
+

ValaLexer

+
+

For Vala source code with preprocessor directives.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:vala, vapi
Filename patterns:*.vala, *.vapi
Mimetypes:text/x-vala
+
+
+
+

Lexers for .net languages

+

BooLexer

+
+

For Boo source code.

+ +++ + + + + + + + +
Short names:boo
Filename patterns:*.boo
Mimetypes:text/x-boo
+
+

CSharpAspxLexer

+
+

Lexer for highligting C# within ASP.NET pages.

+ +++ + + + + + + + +
Short names:aspx-cs
Filename patterns:*.aspx, *.asax, *.ascx, *.ashx, *.asmx, *.axd
Mimetypes:None
+
+

CSharpLexer

+
+

For C# +source code.

+

Additional options accepted:

+
+
unicodelevel
+

Determines which Unicode characters this lexer allows for identifiers. +The possible values are:

+
    +
  • none -- only the ASCII letters and numbers are allowed. This +is the fastest selection.
  • +
  • basic -- all Unicode characters from the specification except +category Lo are allowed.
  • +
  • full -- all Unicode characters as specified in the C# specs +are allowed. Note that this means a considerable slowdown since the +Lo category has more than 40,000 characters in it!
  • +
+

The default value is basic.

+

New in Pygments 0.8.

+
+
+ +++ + + + + + + + +
Short names:csharp, c#
Filename patterns:*.cs
Mimetypes:text/x-csharp
+
+

VbNetAspxLexer

+
+

Lexer for highligting Visual Basic.net within ASP.NET pages.

+ +++ + + + + + + + +
Short names:aspx-vb
Filename patterns:*.aspx, *.asax, *.ascx, *.ashx, *.asmx, *.axd
Mimetypes:None
+
+

VbNetLexer

+
+

For +Visual Basic.NET +source code.

+ +++ + + + + + + + +
Short names:vb.net, vbnet
Filename patterns:*.vb, *.bas
Mimetypes:text/x-vbnet, text/x-vba
+
+
+
+

Lexers for functional languages

+

CommonLispLexer

+
+

A Common Lisp lexer.

+

New in Pygments 0.9.

+ +++ + + + + + + + +
Short names:common-lisp, cl
Filename patterns:*.cl, *.lisp, *.el
Mimetypes:text/x-common-lisp
+
+

ErlangLexer

+
+

For the Erlang functional programming language.

+

Blame Jeremy Thurgood (http://jerith.za.net/).

+

New in Pygments 0.9.

+ +++ + + + + + + + +
Short names:erlang
Filename patterns:*.erl, *.hrl
Mimetypes:text/x-erlang
+
+

ErlangShellLexer

+
+

Shell sessions in erl (for Erlang code).

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:erl
Filename patterns:*.erl-sh
Mimetypes:text/x-erl-shellsession
+
+

HaskellLexer

+
+

A Haskell lexer based on the lexemes defined in the Haskell 98 Report.

+

New in Pygments 0.8.

+ +++ + + + + + + + +
Short names:haskell, hs
Filename patterns:*.hs
Mimetypes:text/x-haskell
+
+

LiterateHaskellLexer

+
+

For Literate Haskell (Bird-style or LaTeX) source.

+

Additional options accepted:

+
+
litstyle
+
If given, must be "bird" or "latex". If not given, the style +is autodetected: if the first non-whitespace character in the source +is a backslash or percent character, LaTeX is assumed, else Bird.
+
+

New in Pygments 0.9.

+ +++ + + + + + + + +
Short names:lhs, literate-haskell
Filename patterns:*.lhs
Mimetypes:text/x-literate-haskell
+
+

OcamlLexer

+
+

For the OCaml language.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:ocaml
Filename patterns:*.ml, *.mli, *.mll, *.mly
Mimetypes:text/x-ocaml
+
+

SchemeLexer

+
+

A Scheme lexer, parsing a stream and outputting the tokens +needed to highlight scheme code. +This lexer could be most probably easily subclassed to parse +other LISP-Dialects like Common Lisp, Emacs Lisp or AutoLisp.

+

This parser is checked with pastes from the LISP pastebin +at http://paste.lisp.org/ to cover as much syntax as possible.

+

It supports the full Scheme syntax as defined in R5RS.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:scheme, scm
Filename patterns:*.scm
Mimetypes:text/x-scheme, application/x-scheme
+
+
+
+

Lexers for math languages

+

MatlabLexer

+
+

For Matlab (or GNU Octave) source code. +Contributed by Ken Schutte <kschutte@csail.mit.edu>.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:matlab, octave
Filename patterns:*.m
Mimetypes:text/matlab
+
+

MatlabSessionLexer

+
+

For Matlab (or GNU Octave) sessions. Modeled after PythonConsoleLexer. +Contributed by Ken Schutte <kschutte@csail.mit.edu>.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:matlabsession
Filename patterns:None
Mimetypes:None
+
+

MuPADLexer

+
+

A MuPAD lexer. +Contributed by Christopher Creutzig <christopher@creutzig.de>.

+

New in Pygments 0.8.

+ +++ + + + + + + + +
Short names:mupad
Filename patterns:*.mu
Mimetypes:None
+
+

NumPyLexer

+
+

A Python lexer recognizing Numerical Python builtins.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:numpy
Filename patterns:None
Mimetypes:None
+
+

SLexer

+
+

For S, S-plus, and R source code.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:splus, s, r
Filename patterns:*.S, *.R
Mimetypes:text/S-plus, text/S, text/R
+
+
+
+

Lexers for other languages

+

ABAPLexer

+
+

Lexer for ABAP, SAP's integrated language.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:abap
Filename patterns:*.abap
Mimetypes:text/x-abap
+
+

AppleScriptLexer

+
+

For AppleScript source code, +including AppleScript Studio. +Contributed by Andreas Amann <aamann@mac.com>.

+ +++ + + + + + + + +
Short names:applescript
Filename patterns:*.applescript
Mimetypes:None
+
+

BashLexer

+
+

Lexer for (ba)sh shell scripts.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:bash, sh
Filename patterns:*.sh, *.ebuild, *.eclass
Mimetypes:application/x-sh, application/x-shellscript
+
+

BashSessionLexer

+
+

Lexer for simplistic shell sessions.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:console
Filename patterns:*.sh-session
Mimetypes:application/x-shell-session
+
+

BatchLexer

+
+

Lexer for the DOS/Windows Batch file format.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:bat
Filename patterns:*.bat, *.cmd
Mimetypes:application/x-dos-batch
+
+

BefungeLexer

+
+

Lexer for the esoteric Befunge +language.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:befunge
Filename patterns:*.befunge
Mimetypes:application/x-befunge
+
+

BrainfuckLexer

+
+

Lexer for the esoteric BrainFuck +language.

+ +++ + + + + + + + +
Short names:brainfuck, bf
Filename patterns:*.bf, *.b
Mimetypes:application/x-brainfuck
+
+

GnuplotLexer

+
+

For Gnuplot plotting scripts.

+

New in Pygments 0.11.

+ +++ + + + + + + + +
Short names:gnuplot
Filename patterns:*.plot, *.plt
Mimetypes:text/x-gnuplot
+
+

LogtalkLexer

+
+

For Logtalk source code.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:logtalk
Filename patterns:*.lgt
Mimetypes:text/x-logtalk
+
+

MOOCodeLexer

+
+

For MOOCode (the MOO scripting +language).

+

New in Pygments 0.9.

+ +++ + + + + + + + +
Short names:moocode
Filename patterns:*.moo
Mimetypes:text/x-moocode
+
+

ModelicaLexer

+
+

For Modelica source code.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:modelica
Filename patterns:*.mo
Mimetypes:text/x-modelica
+
+

MySqlLexer

+
+

Special lexer for MySQL.

+ +++ + + + + + + + +
Short names:mysql
Filename patterns:None
Mimetypes:text/x-mysql
+
+

NewspeakLexer

+
+

For Newspeak <http://newspeaklanguage.org/> syntax.

+ +++ + + + + + + + +
Short names:newspeak
Filename patterns:*.ns2
Mimetypes:text/x-newspeak
+
+

PovrayLexer

+
+

For Persistence of Vision Raytracer files.

+

New in Pygments 0.11.

+ +++ + + + + + + + +
Short names:pov
Filename patterns:*.pov, *.inc
Mimetypes:text/x-povray
+
+

RebolLexer

+
+

A REBOL lexer.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:rebol
Filename patterns:*.r, *.r3
Mimetypes:text/x-rebol
+
+

RedcodeLexer

+
+

A simple Redcode lexer based on ICWS'94. +Contributed by Adam Blinkinsop <blinks@acm.org>.

+

New in Pygments 0.8.

+ +++ + + + + + + + +
Short names:redcode
Filename patterns:*.cw
Mimetypes:None
+
+

SmalltalkLexer

+
+

For Smalltalk syntax. +Contributed by Stefan Matthias Aust. +Rewritten by Nils Winter.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:smalltalk, squeak
Filename patterns:*.st
Mimetypes:text/x-smalltalk
+
+

SqlLexer

+
+

Lexer for Structured Query Language. Currently, this lexer does +not recognize any special syntax except ANSI SQL.

+ +++ + + + + + + + +
Short names:sql
Filename patterns:*.sql
Mimetypes:text/x-sql
+
+

SqliteConsoleLexer

+
+

Lexer for example sessions using sqlite3.

+

New in Pygments 0.11.

+ +++ + + + + + + + +
Short names:sqlite3
Filename patterns:*.sqlite3-console
Mimetypes:text/x-sqlite3-console
+
+

TcshLexer

+
+

Lexer for tcsh scripts.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:tcsh, csh
Filename patterns:*.tcsh, *.csh
Mimetypes:application/x-csh
+
+
+
+

Lexers for parser generators

+

AntlrActionScriptLexer

+
+

ANTLR with ActionScript Target

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:antlr-as, antlr-actionscript
Filename patterns:*.G, *.g
Mimetypes:None
+
+

AntlrCSharpLexer

+
+

ANTLR with C# Target

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:antlr-csharp, antlr-c#
Filename patterns:*.G, *.g
Mimetypes:None
+
+

AntlrCppLexer

+
+

ANTLR with CPP Target

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:antlr-cpp
Filename patterns:*.G, *.g
Mimetypes:None
+
+

AntlrJavaLexer

+
+

ANTLR with Java Target

+

New in Pygments 1.1

+ +++ + + + + + + + +
Short names:antlr-java
Filename patterns:*.G, *.g
Mimetypes:None
+
+

AntlrLexer

+
+

Generic ANTLR Lexer. +Should not be called directly, instead +use DelegatingLexer for your target language.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:antlr
Filename patterns:None
Mimetypes:None
+
+

AntlrObjectiveCLexer

+
+

ANTLR with Objective-C Target

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:antlr-objc
Filename patterns:*.G, *.g
Mimetypes:None
+
+

AntlrPerlLexer

+
+

ANTLR with Perl Target

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:antlr-perl
Filename patterns:*.G, *.g
Mimetypes:None
+
+

AntlrPythonLexer

+
+

ANTLR with Python Target

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:antlr-python
Filename patterns:*.G, *.g
Mimetypes:None
+
+

AntlrRubyLexer

+
+

ANTLR with Ruby Target

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:antlr-ruby, antlr-rb
Filename patterns:*.G, *.g
Mimetypes:None
+
+

RagelCLexer

+
+

A lexer for Ragel in a C host file.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:ragel-c
Filename patterns:*.rl
Mimetypes:None
+
+

RagelCppLexer

+
+

A lexer for Ragel in a CPP host file.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:ragel-cpp
Filename patterns:*.rl
Mimetypes:None
+
+

RagelDLexer

+
+

A lexer for Ragel in a D host file.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:ragel-d
Filename patterns:*.rl
Mimetypes:None
+
+

RagelEmbeddedLexer

+
+

A lexer for Ragel embedded in a host language file.

+

This will only highlight Ragel statements. If you want host language +highlighting then call the language-specific Ragel lexer.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:ragel-em
Filename patterns:*.rl
Mimetypes:None
+
+

RagelJavaLexer

+
+

A lexer for Ragel in a Java host file.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:ragel-java
Filename patterns:*.rl
Mimetypes:None
+
+

RagelLexer

+
+

A pure Ragel lexer. Use this for +fragments of Ragel. For .rl files, use RagelEmbeddedLexer instead +(or one of the language-specific subclasses).

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:ragel
Filename patterns:None
Mimetypes:None
+
+

RagelObjectiveCLexer

+
+

A lexer for Ragel in an Objective C host file.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:ragel-objc
Filename patterns:*.rl
Mimetypes:None
+
+

RagelRubyLexer

+
+

A lexer for Ragel in a Ruby host file.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:ragel-ruby, ragel-rb
Filename patterns:*.rl
Mimetypes:None
+
+
+
+

Special lexers

+

RawTokenLexer

+
+

Recreate a token stream formatted with the RawTokenFormatter. This +lexer raises exceptions during parsing if the token stream in the +file is malformed.

+

Additional options accepted:

+
+
compress
+
If set to "gz" or "bz2", decompress the token stream with +the given compression algorithm before lexing (default: "").
+
+ +++ + + + + + + + +
Short names:raw
Filename patterns:None
Mimetypes:application/x-pygments-tokens
+
+

TextLexer

+
+

"Null" lexer, doesn't highlight anything.

+ +++ + + + + + + + +
Short names:text
Filename patterns:*.txt
Mimetypes:text/plain
+
+
+
+

Lexers for various template engines' markup

+

CheetahHtmlLexer

+
+

Subclass of the CheetahLexer that highlights unlexer data +with the HtmlLexer.

+ +++ + + + + + + + +
Short names:html+cheetah, html+spitfire
Filename patterns:None
Mimetypes:text/html+cheetah, text/html+spitfire
+
+

CheetahJavascriptLexer

+
+

Subclass of the CheetahLexer that highlights unlexer data +with the JavascriptLexer.

+ +++ + + + + + + + +
Short names:js+cheetah, javascript+cheetah, js+spitfire, javascript+spitfire
Filename patterns:None
Mimetypes:application/x-javascript+cheetah, text/x-javascript+cheetah, text/javascript+cheetah, application/x-javascript+spitfire, text/x-javascript+spitfire, text/javascript+spitfire
+
+

CheetahLexer

+
+

Generic cheetah templates lexer. Code that isn't Cheetah +markup is yielded as Token.Other. This also works for +spitfire templates which use the same syntax.

+ +++ + + + + + + + +
Short names:cheetah, spitfire
Filename patterns:*.tmpl, *.spt
Mimetypes:application/x-cheetah, application/x-spitfire
+
+

CheetahXmlLexer

+
+

Subclass of the CheetahLexer that highlights unlexer data +with the XmlLexer.

+ +++ + + + + + + + +
Short names:xml+cheetah, xml+spitfire
Filename patterns:None
Mimetypes:application/xml+cheetah, application/xml+spitfire
+
+

CssDjangoLexer

+
+

Subclass of the DjangoLexer that highlights unlexed data with the +CssLexer.

+ +++ + + + + + + + +
Short names:css+django, css+jinja
Filename patterns:None
Mimetypes:text/css+django, text/css+jinja
+
+

CssErbLexer

+
+

Subclass of ErbLexer which highlights unlexed data with the CssLexer.

+ +++ + + + + + + + +
Short names:css+erb, css+ruby
Filename patterns:None
Mimetypes:text/css+ruby
+
+

CssGenshiLexer

+
+

A lexer that highlights CSS definitions in genshi text templates.

+ +++ + + + + + + + +
Short names:css+genshitext, css+genshi
Filename patterns:None
Mimetypes:text/css+genshi
+
+

CssPhpLexer

+
+

Subclass of PhpLexer which highlights unmatched data with the CssLexer.

+ +++ + + + + + + + +
Short names:css+php
Filename patterns:None
Mimetypes:text/css+php
+
+

CssSmartyLexer

+
+

Subclass of the SmartyLexer that highlights unlexed data with the +CssLexer.

+ +++ + + + + + + + +
Short names:css+smarty
Filename patterns:None
Mimetypes:text/css+smarty
+
+

DjangoLexer

+
+

Generic django +and jinja template lexer.

+

It just highlights django/jinja code between the preprocessor directives, +other data is left untouched by the lexer.

+ +++ + + + + + + + +
Short names:django, jinja
Filename patterns:None
Mimetypes:application/x-django-templating, application/x-jinja
+
+

ErbLexer

+
+

Generic ERB (Ruby Templating) +lexer.

+

Just highlights ruby code between the preprocessor directives, other data +is left untouched by the lexer.

+

All options are also forwarded to the RubyLexer.

+ +++ + + + + + + + +
Short names:erb
Filename patterns:None
Mimetypes:application/x-ruby-templating
+
+

EvoqueHtmlLexer

+
+

Subclass of the EvoqueLexer that highlights unlexed data with the +HtmlLexer.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:html+evoque
Filename patterns:*.html
Mimetypes:text/html+evoque
+
+

EvoqueLexer

+
+

For files using the Evoque templating system.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:evoque
Filename patterns:*.evoque
Mimetypes:application/x-evoque
+
+

EvoqueXmlLexer

+
+

Subclass of the EvoqueLexer that highlights unlexed data with the +XmlLexer.

+

New in Pygments 1.1.

+ +++ + + + + + + + +
Short names:xml+evoque
Filename patterns:*.xml
Mimetypes:application/xml+evoque
+
+

GenshiLexer

+
+

A lexer that highlights genshi and +kid kid XML templates.

+ +++ + + + + + + + +
Short names:genshi, kid, xml+genshi, xml+kid
Filename patterns:*.kid
Mimetypes:application/x-genshi, application/x-kid
+
+

GenshiTextLexer

+
+

A lexer that highlights genshi text +templates.

+ +++ + + + + + + + +
Short names:genshitext
Filename patterns:None
Mimetypes:application/x-genshi-text, text/x-genshi
+
+

HtmlDjangoLexer

+
+

Subclass of the DjangoLexer that highighlights unlexed data with the +HtmlLexer.

+

Nested Javascript and CSS is highlighted too.

+ +++ + + + + + + + +
Short names:html+django, html+jinja
Filename patterns:None
Mimetypes:text/html+django, text/html+jinja
+
+

HtmlGenshiLexer

+
+

A lexer that highlights genshi and +kid kid HTML templates.

+ +++ + + + + + + + +
Short names:html+genshi, html+kid
Filename patterns:None
Mimetypes:text/html+genshi
+
+

HtmlPhpLexer

+
+

Subclass of PhpLexer that highlights unhandled data with the HtmlLexer.

+

Nested Javascript and CSS is highlighted too.

+ +++ + + + + + + + +
Short names:html+php
Filename patterns:*.phtml
Mimetypes:application/x-php, application/x-httpd-php, application/x-httpd-php3, application/x-httpd-php4, application/x-httpd-php5
+
+

HtmlSmartyLexer

+
+

Subclass of the SmartyLexer that highighlights unlexed data with the +HtmlLexer.

+

Nested Javascript and CSS is highlighted too.

+ +++ + + + + + + + +
Short names:html+smarty
Filename patterns:None
Mimetypes:text/html+smarty
+
+

JavascriptDjangoLexer

+
+

Subclass of the DjangoLexer that highlights unlexed data with the +JavascriptLexer.

+ +++ + + + + + + + +
Short names:js+django, javascript+django, js+jinja, javascript+jinja
Filename patterns:None
Mimetypes:application/x-javascript+django, application/x-javascript+jinja, text/x-javascript+django, text/x-javascript+jinja, text/javascript+django, text/javascript+jinja
+
+

JavascriptErbLexer

+
+

Subclass of ErbLexer which highlights unlexed data with the +JavascriptLexer.

+ +++ + + + + + + + +
Short names:js+erb, javascript+erb, js+ruby, javascript+ruby
Filename patterns:None
Mimetypes:application/x-javascript+ruby, text/x-javascript+ruby, text/javascript+ruby
+
+

JavascriptGenshiLexer

+
+

A lexer that highlights javascript code in genshi text templates.

+ +++ + + + + + + + +
Short names:js+genshitext, js+genshi, javascript+genshitext, javascript+genshi
Filename patterns:None
Mimetypes:application/x-javascript+genshi, text/x-javascript+genshi, text/javascript+genshi
+
+

JavascriptPhpLexer

+
+

Subclass of PhpLexer which highlights unmatched data with the +JavascriptLexer.

+ +++ + + + + + + + +
Short names:js+php, javascript+php
Filename patterns:None
Mimetypes:application/x-javascript+php, text/x-javascript+php, text/javascript+php
+
+

JavascriptSmartyLexer

+
+

Subclass of the SmartyLexer that highlights unlexed data with the +JavascriptLexer.

+ +++ + + + + + + + +
Short names:js+smarty, javascript+smarty
Filename patterns:None
Mimetypes:application/x-javascript+smarty, text/x-javascript+smarty, text/javascript+smarty
+
+

JspLexer

+
+

Lexer for Java Server Pages.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:jsp
Filename patterns:*.jsp
Mimetypes:application/x-jsp
+
+

MakoCssLexer

+
+

Subclass of the MakoLexer that highlights unlexer data +with the CssLexer.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:css+mako
Filename patterns:None
Mimetypes:text/css+mako
+
+

MakoHtmlLexer

+
+

Subclass of the MakoLexer that highlights unlexed data +with the HtmlLexer.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:html+mako
Filename patterns:None
Mimetypes:text/html+mako
+
+

MakoJavascriptLexer

+
+

Subclass of the MakoLexer that highlights unlexer data +with the JavascriptLexer.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:js+mako, javascript+mako
Filename patterns:None
Mimetypes:application/x-javascript+mako, text/x-javascript+mako, text/javascript+mako
+
+

MakoLexer

+
+

Generic mako templates lexer. Code that isn't Mako +markup is yielded as Token.Other.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:mako
Filename patterns:*.mao
Mimetypes:application/x-mako
+
+

MakoXmlLexer

+
+

Subclass of the MakoLexer that highlights unlexer data +with the XmlLexer.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:xml+mako
Filename patterns:None
Mimetypes:application/xml+mako
+
+

MyghtyCssLexer

+
+

Subclass of the MyghtyLexer that highlights unlexer data +with the CssLexer.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:css+myghty
Filename patterns:None
Mimetypes:text/css+myghty
+
+

MyghtyHtmlLexer

+
+

Subclass of the MyghtyLexer that highlights unlexer data +with the HtmlLexer.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:html+myghty
Filename patterns:None
Mimetypes:text/html+myghty
+
+

MyghtyJavascriptLexer

+
+

Subclass of the MyghtyLexer that highlights unlexer data +with the JavascriptLexer.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:js+myghty, javascript+myghty
Filename patterns:None
Mimetypes:application/x-javascript+myghty, text/x-javascript+myghty, text/javascript+mygthy
+
+

MyghtyLexer

+
+

Generic myghty templates lexer. Code that isn't Myghty +markup is yielded as Token.Other.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:myghty
Filename patterns:*.myt, autodelegate
Mimetypes:application/x-myghty
+
+

MyghtyXmlLexer

+
+

Subclass of the MyghtyLexer that highlights unlexer data +with the XmlLexer.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:xml+myghty
Filename patterns:None
Mimetypes:application/xml+myghty
+
+

RhtmlLexer

+
+

Subclass of the ERB lexer that highlights the unlexed data with the +html lexer.

+

Nested Javascript and CSS is highlighted too.

+ +++ + + + + + + + +
Short names:rhtml, html+erb, html+ruby
Filename patterns:*.rhtml
Mimetypes:text/html+ruby
+
+

SmartyLexer

+
+

Generic Smarty template lexer.

+

Just highlights smarty code between the preprocessor directives, other +data is left untouched by the lexer.

+ +++ + + + + + + + +
Short names:smarty
Filename patterns:*.tpl
Mimetypes:application/x-smarty
+
+

XmlDjangoLexer

+
+

Subclass of the DjangoLexer that highlights unlexed data with the +XmlLexer.

+ +++ + + + + + + + +
Short names:xml+django, xml+jinja
Filename patterns:None
Mimetypes:application/xml+django, application/xml+jinja
+
+

XmlErbLexer

+
+

Subclass of ErbLexer which highlights data outside preprocessor +directives with the XmlLexer.

+ +++ + + + + + + + +
Short names:xml+erb, xml+ruby
Filename patterns:None
Mimetypes:application/xml+ruby
+
+

XmlPhpLexer

+
+

Subclass of PhpLexer that higlights unhandled data with the XmlLexer.

+ +++ + + + + + + + +
Short names:xml+php
Filename patterns:None
Mimetypes:application/xml+php
+
+

XmlSmartyLexer

+
+

Subclass of the SmartyLexer that highlights unlexed data with the +XmlLexer.

+ +++ + + + + + + + +
Short names:xml+smarty
Filename patterns:None
Mimetypes:application/xml+smarty
+
+
+
+

Lexers for non-source code file types

+

ApacheConfLexer

+
+

Lexer for configuration files following the Apache config file +format.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:apacheconf, aconf, apache
Filename patterns:.htaccess, apache.conf, apache2.conf
Mimetypes:text/x-apacheconf
+
+

BBCodeLexer

+
+

A lexer that highlights BBCode(-like) syntax.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:bbcode
Filename patterns:None
Mimetypes:text/x-bbcode
+
+

BaseMakefileLexer

+
+

Lexer for simple Makefiles (no preprocessing).

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:basemake
Filename patterns:None
Mimetypes:None
+
+

DarcsPatchLexer

+
+

DarcsPatchLexer is a lexer for the various versions of the darcs patch +format. Examples of this format are derived by commands such as +darcs annotate --patch and darcs send.

+

New in Pygments 0.10.

+ +++ + + + + + + + +
Short names:dpatch
Filename patterns:*.dpatch, *.darcspatch
Mimetypes:None
+
+

DebianControlLexer

+
+

Lexer for Debian control files and apt-cache show <pkg> outputs.

+

New in Pygments 0.9.

+ +++ + + + + + + + +
Short names:control
Filename patterns:control
Mimetypes:None
+
+

DiffLexer

+
+

Lexer for unified or context-style diffs or patches.

+ +++ + + + + + + + +
Short names:diff, udiff
Filename patterns:*.diff, *.patch
Mimetypes:text/x-diff, text/x-patch
+
+

GettextLexer

+
+

Lexer for Gettext catalog files.

+

New in Pygments 0.9.

+ +++ + + + + + + + +
Short names:pot, po
Filename patterns:*.pot, *.po
Mimetypes:application/x-gettext, text/x-gettext, text/gettext
+
+

GroffLexer

+
+

Lexer for the (g)roff typesetting language, supporting groff +extensions. Mainly useful for highlighting manpage sources.

+

New in Pygments 0.6.

+ +++ + + + + + + + +
Short names:groff, nroff, man
Filename patterns:*.[1234567], *.man
Mimetypes:application/x-troff, text/troff
+
+

IniLexer

+
+

Lexer for configuration files in INI style.

+ +++ + + + + + + + +
Short names:ini, cfg
Filename patterns:*.ini, *.cfg, *.properties
Mimetypes:text/x-ini
+
+

IrcLogsLexer

+
+

Lexer for IRC logs in irssi, xchat or weechat style.

+ +++ + + + + + + + +
Short names:irc
Filename patterns:*.weechatlog
Mimetypes:text/x-irclog
+
+

LighttpdConfLexer

+
+

Lexer for Lighttpd configuration files.

+

New in Pygments 0.11.

+ +++ + + + + + + + +
Short names:lighty, lighttpd
Filename patterns:None
Mimetypes:text/x-lighttpd-conf
+
+

MakefileLexer

+
+

Lexer for BSD and GNU make extensions (lenient enough to handle both in +the same file even).

+

Rewritten in Pygments 0.10.

+ +++ + + + + + + + +
Short names:make, makefile, mf, bsdmake
Filename patterns:*.mak, Makefile, makefile, Makefile.*, GNUmakefile
Mimetypes:text/x-makefile
+
+

MoinWikiLexer

+
+

For MoinMoin (and Trac) Wiki markup.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:trac-wiki, moin
Filename patterns:None
Mimetypes:text/x-trac-wiki
+
+

NginxConfLexer

+
+

Lexer for Nginx configuration files.

+

New in Pygments 0.11.

+ +++ + + + + + + + +
Short names:nginx
Filename patterns:None
Mimetypes:text/x-nginx-conf
+
+

RstLexer

+
+

For reStructuredText markup.

+

New in Pygments 0.7.

+

Additional options accepted:

+
+
handlecodeblocks
+
Highlight the contents of .. sourcecode:: langauge and +.. code:: language directives with a lexer for the given +language (default: True). New in Pygments 0.8.
+
+ +++ + + + + + + + +
Short names:rst, rest, restructuredtext
Filename patterns:*.rst, *.rest
Mimetypes:text/x-rst, text/prs.fallenstein.rst
+
+

SourcesListLexer

+
+

Lexer that highlights debian sources.list files.

+

New in Pygments 0.7.

+ +++ + + + + + + + +
Short names:sourceslist, sources.list
Filename patterns:sources.list
Mimetypes:None
+
+

SquidConfLexer

+
+

Lexer for squid configuration files.

+

New in Pygments 0.9.

+ +++ + + + + + + + +
Short names:squidconf, squid.conf, squid
Filename patterns:squid.conf
Mimetypes:text/x-squidconf
+
+

TexLexer

+
+

Lexer for the TeX and LaTeX typesetting languages.

+ +++ + + + + + + + +
Short names:tex, latex
Filename patterns:*.tex, *.aux, *.toc
Mimetypes:text/x-tex, text/x-latex
+
+

VimLexer

+
+

Lexer for VimL script files.

+

New in Pygments 0.8.

+ +++ + + + + + + + +
Short names:vim
Filename patterns:*.vim, .vimrc
Mimetypes:text/x-vim
+
+

YamlLexer

+
+

Lexer for YAML, a human-friendly data serialization +language.

+

New in Pygments 0.11.

+ +++ + + + + + + + +
Short names:yaml
Filename patterns:*.yaml, *.yml
Mimetypes:text/x-yaml
+
+
+ +
+

Iterating over all lexers

+

New in Pygments 0.6.

+

To get all lexers (both the builtin and the plugin ones), you can +use the get_all_lexers() function from the pygments.lexers +module:

+
>>> from pygments.lexers import get_all_lexers
+>>> i = get_all_lexers()
+>>> i.next()
+('Diff', ('diff',), ('*.diff', '*.patch'), ('text/x-diff', 'text/x-patch'))
+>>> i.next()
+('Delphi', ('delphi', 'objectpascal', 'pas', 'pascal'), ('*.pas',), ('text/x-pascal',))
+>>> i.next()
+('XML+Ruby', ('xml+erb', 'xml+ruby'), (), ())
+
+

As you can see, the return value is an iterator which yields tuples +in the form (name, aliases, filetypes, mimetypes).

+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/moinmoin.html b/docs/build/moinmoin.html new file mode 100644 index 0000000..5d4aa9b --- /dev/null +++ b/docs/build/moinmoin.html @@ -0,0 +1,245 @@ + + + + Using Pygments with MoinMoin — Pygments + + + + +
+

Pygments

+

Using Pygments with MoinMoin

+ + « Back To Index + + + +

From Pygments 0.7, the source distribution ships a Moin parser plugin that +can be used to get Pygments highlighting in Moin wiki pages.

+

To use it, copy the file external/moin-parser.py from the Pygments +distribution to the data/plugin/parser subdirectory of your Moin instance. +Edit the options at the top of the file (currently ATTACHMENTS and +INLINESTYLES) and rename the file to the name that the parser directive +should have. For example, if you name the file code.py, you can get a +highlighted Python code sample with this Wiki markup:

+
+{{{
+#!code python
+[...]
+}}}
+
+

where python is the Pygments name of the lexer to use.

+

Additionally, if you set the ATTACHMENTS option to True, Pygments will also +be called for all attachments for whose filenames there is no other parser +registered.

+

You are responsible for including CSS rules that will map the Pygments CSS +classes to colors. You can output a stylesheet file with pygmentize, put it +into the htdocs directory of your Moin instance and then include it in the +stylesheets configuration option in the Moin config, e.g.:

+
+stylesheets = [('screen', '/htdocs/pygments.css')]
+
+

If you do not want to do that and are willing to accept larger HTML output, you +can set the INLINESTYLES option to True.

+ +
+ + + \ No newline at end of file diff --git a/docs/build/plugins.html b/docs/build/plugins.html new file mode 100644 index 0000000..6ebf441 --- /dev/null +++ b/docs/build/plugins.html @@ -0,0 +1,294 @@ + + + + Register Plugins — Pygments + + + + +
+

Pygments

+

Register Plugins

+ + « Back To Index + + +
+

Contents

+ +
+ +

If you want to extend Pygments without hacking the sources, but want to +use the lexer/formatter/style/filter lookup functions (lexers.get_lexer_by_name +et al.), you can use setuptools entrypoints to add new lexers, formatters +or styles as if they were in the Pygments core.

+

That means you can use your highlighter modules with the pygmentize script, +which relies on the mentioned functions.

+
+

Entrypoints

+

Here is a list of setuptools entrypoints that Pygments understands:

+

pygments.lexers

+
+

This entrypoint is used for adding new lexers to the Pygments core. +The name of the entrypoint values doesn't really matter, Pygments extracts +required metadata from the class definition:

+
[pygments.lexers]
+yourlexer = yourmodule:YourLexer
+
+

Note that you have to define name, aliases and filename +attributes so that you can use the highlighter from the command line:

+
class YourLexer(...):
+    name = 'Name Of Your Lexer'
+    aliases = ['alias']
+    filenames = ['*.ext']
+
+
+

pygments.formatters

+
+

You can use this entrypoint to add new formatters to Pygments. The +name of an entrypoint item is the name of the formatter. If you +prefix the name with a slash it's used as a filename pattern:

+
[pygments.formatters]
+yourformatter = yourmodule:YourFormatter
+/.ext = yourmodule:YourFormatter
+
+
+

pygments.styles

+
+

To add a new style you can use this entrypoint. The name of the entrypoint +is the name of the style:

+
[pygments.styles]
+yourstyle = yourmodule:YourStyle
+
+
+

pygments.filters

+
+

Use this entrypoint to register a new filter. The name of the +entrypoint is the name of the filter:

+
[pygments.filters]
+yourfilter = yourmodule:YourFilter
+
+
+
+
+

How To Use Entrypoints

+

This documentation doesn't explain how to use those entrypoints because this is +covered in the setuptools documentation. That page should cover everything +you need to write a plugin. Also this blog entry might be interesting.

+
+
+

Extending The Core

+

If you have written a Pygments plugin that is open source, please inform us +about that. There is a high chance that we'll add it to the Pygments +distribution.

+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/quickstart.html b/docs/build/quickstart.html new file mode 100644 index 0000000..c7c018d --- /dev/null +++ b/docs/build/quickstart.html @@ -0,0 +1,390 @@ + + + + Introduction and Quickstart — Pygments + + + + +
+

Pygments

+

Introduction and Quickstart

+ + « Back To Index + + + + + +

Welcome to Pygments! This document explains the basic concepts and terms and +gives a few examples of how to use the library.

+
+

Architecture

+

There are four types of components that work together highlighting a piece of +code:

+
    +
  • A lexer splits the source into tokens, fragments of the source that +have a token type that determines what the text represents semantically +(e.g., keyword, string, or comment). There is a lexer for every language +or markup format that Pygments supports.
  • +
  • The token stream can be piped through filters, which usually modify +the token types or text fragments, e.g. uppercasing all keywords.
  • +
  • A formatter then takes the token stream and writes it to an output +file, in a format such as HTML, LaTeX or RTF.
  • +
  • While writing the output, a style determines how to highlight all the +different token types. It maps them to attributes like "red and bold".
  • +
+
+
+

Example

+

Here is a small example for highlighting Python code:

+
from pygments import highlight
+from pygments.lexers import PythonLexer
+from pygments.formatters import HtmlFormatter
+
+code = 'print "Hello World"'
+print highlight(code, PythonLexer(), HtmlFormatter())
+
+

which prints something like this:

+
<div class="highlight">
+<pre><span class="k">print</span> <span class="s">&quot;Hello World&quot;</span></pre>
+</div>
+
+

As you can see, Pygments uses CSS classes (by default, but you can change that) +instead of inline styles in order to avoid outputting redundant style information over +and over. A CSS stylesheet that contains all CSS classes possibly used in the output +can be produced by:

+
print HtmlFormatter().get_style_defs('.highlight')
+
+

The argument to get_style_defs is used as an additional CSS selector: the output +may look like this:

+
.highlight .k { color: #AA22FF; font-weight: bold }
+.highlight .s { color: #BB4444 }
+...
+
+
+
+

Options

+

The highlight() function supports a fourth argument called outfile, it must be +a file object if given. The formatted output will then be written to this file +instead of being returned as a string.

+

Lexers and formatters both support options. They are given to them as keyword +arguments either to the class or to the lookup method:

+
from pygments import highlight
+from pygments.lexers import get_lexer_by_name
+from pygments.formatters import HtmlFormatter
+
+lexer = get_lexer_by_name("python", stripall=True)
+formatter = HtmlFormatter(linenos=True, cssclass="source")
+result = highlight(code, lexer, formatter)
+
+

This makes the lexer strip all leading and trailing whitespace from the input +(stripall option), lets the formatter output line numbers (linenos option), +and sets the wrapping <div>'s class to source (instead of +highlight).

+

Important options include:

+
+
encoding : for lexers and formatters
+
Since Pygments uses Unicode strings internally, this determines which +encoding will be used to convert to or from byte strings.
+
style : for formatters
+
The name of the style to use when writing the output.
+
+

For an overview of builtin lexers and formatters and their options, visit the +lexer and formatters lists.

+

For a documentation on filters, see this page.

+
+
+

Lexer and formatter lookup

+

If you want to lookup a built-in lexer by its alias or a filename, you can use +one of the following methods:

+
>>> from pygments.lexers import (get_lexer_by_name,
+...     get_lexer_for_filename, get_lexer_for_mimetype)
+
+>>> get_lexer_by_name('python')
+<pygments.lexers.PythonLexer>
+
+>>> get_lexer_for_filename('spam.rb')
+<pygments.lexers.RubyLexer>
+
+>>> get_lexer_for_mimetype('text/x-perl')
+<pygments.lexers.PerlLexer>
+
+

All these functions accept keyword arguments; they will be passed to the lexer +as options.

+

A similar API is available for formatters: use get_formatter_by_name() and +get_formatter_for_filename() from the pygments.formatters module +for this purpose.

+
+
+

Guessing lexers

+

If you don't know the content of the file, or you want to highlight a file +whose extension is ambiguous, such as .html (which could contain plain HTML +or some template tags), use these functions:

+
>>> from pygments.lexers import guess_lexer, guess_lexer_for_filename
+
+>>> guess_lexer('#!/usr/bin/python\nprint "Hello World!"')
+<pygments.lexers.PythonLexer>
+
+>>> guess_lexer_for_filename('test.py', 'print "Hello World!"')
+<pygments.lexers.PythonLexer>
+
+

guess_lexer() passes the given content to the lexer classes' analyse_text() +method and returns the one for which it returns the highest number.

+

All lexers have two different filename pattern lists: the primary and the +secondary one. The get_lexer_for_filename() function only uses the primary +list, whose entries are supposed to be unique among all lexers. +guess_lexer_for_filename(), however, will first loop through all lexers and +look at the primary and secondary filename patterns if the filename matches. +If only one lexer matches, it is returned, else the guessing mechanism of +guess_lexer() is used with the matching lexers.

+

As usual, keyword arguments to these functions are given to the created lexer +as options.

+
+
+

Command line usage

+

You can use Pygments from the command line, using the pygmentize script:

+
+$ pygmentize test.py
+
+

will highlight the Python file test.py using ANSI escape sequences +(a.k.a. terminal colors) and print the result to standard output.

+

To output HTML, use the -f option:

+
+$ pygmentize -f html -o test.html test.py
+
+

to write an HTML-highlighted version of test.py to the file test.html. +Note that it will only be a snippet of HTML, if you want a full HTML document, +use the "full" option:

+
+$ pygmentize -f html -O full -o test.html test.py
+
+

This will produce a full HTML document with included stylesheet.

+

A style can be selected with -O style=<name>.

+

If you need a stylesheet for an existing HTML file using Pygments CSS classes, +it can be created with:

+
+$ pygmentize -S default -f html > style.css
+
+

where default is the style name.

+

More options and tricks and be found in the command line reference.

+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/rstdirective.html b/docs/build/rstdirective.html new file mode 100644 index 0000000..5468512 --- /dev/null +++ b/docs/build/rstdirective.html @@ -0,0 +1,229 @@ + + + + Using Pygments in ReST documents — Pygments + + + + +
+

Pygments

+

Using Pygments in ReST documents

+ + « Back To Index + + + +

Many Python people use ReST for documentation their sourcecode, programs, +scripts et cetera. This also means that documentation often includes sourcecode +samples or snippets.

+

You can easily enable Pygments support for your ReST texts using a custom +directive -- this is also how this documentation displays source code.

+

From Pygments 0.9, the directive is shipped in the distribution as +external/rst-directive.py. You can copy and adapt this code to your liking.

+ + +
+ + + \ No newline at end of file diff --git a/docs/build/styles.html b/docs/build/styles.html new file mode 100644 index 0000000..913c33b --- /dev/null +++ b/docs/build/styles.html @@ -0,0 +1,341 @@ + + + + Styles — Pygments + + + + +
+

Pygments

+

Styles

+ + « Back To Index + + + + + +

Pygments comes with some builtin styles that work for both the HTML and +LaTeX formatter.

+

The builtin styles can be looked up with the get_style_by_name function:

+
>>> from pygments.styles import get_style_by_name
+>>> get_style_by_name('colorful')
+<class 'pygments.styles.colorful.ColorfulStyle'>
+
+

You can pass a instance of a Style class to a formatter as the style +option in form of a string:

+
>>> from pygments.styles import get_style_by_name
+>>> HtmlFormatter(style='colorful').style
+<class 'pygments.styles.colorful.ColorfulStyle'>
+
+

Or you can also import your own style (which must be a subclass of +pygments.style.Style) and pass it to the formatter:

+
>>> from yourapp.yourmodule import YourStyle
+>>> HtmlFormatter(style=YourStyle).style
+<class 'yourapp.yourmodule.YourStyle'>
+
+
+

Creating Own Styles

+

So, how to create a style? All you have to do is to subclass Style and +define some styles:

+
from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+     Number, Operator, Generic
+
+class YourStyle(Style):
+    default_style = ""
+    styles = {
+        Comment:                'italic #888',
+        Keyword:                'bold #005',
+        Name:                   '#f00',
+        Name.Function:          '#0f0',
+        Name.Class:             'bold #0f0',
+        String:                 'bg:#eee #111'
+    }
+
+

That's it. There are just a few rules. When you define a style for Name +the style automatically also affects Name.Function and so on. If you +defined 'bold' and you don't want boldface for a subtoken use 'nobold'.

+

(Philosophy: the styles aren't written in CSS syntax since this way +they can be used for a variety of formatters.)

+

default_style is the style inherited by all token types.

+

To make the style usable for Pygments, you must

+
    +
  • either register it as a plugin (see the plugin docs)
  • +
  • or drop it into the styles subpackage of your Pygments distribution one style +class per style, where the file name is the style name and the class name is +StylenameClass. For example, if your style should be called +"mondrian", name the class MondrianStyle, put it into the file +mondrian.py and this file into the pygments.styles subpackage +directory.
  • +
+
+
+

Style Rules

+

Here a small overview of all allowed styles:

+
+
bold
+
render text as bold
+
nobold
+
don't render text as bold (to prevent subtokens behing highlighted bold)
+
italic
+
render text italic
+
noitalic
+
don't render text as italic
+
underline
+
render text underlined
+
nounderline
+
don't render text underlined
+
bg:
+
transparent background
+
bg:#000000
+
background color (black)
+
border:
+
no border
+
border:#ffffff
+
border color (white)
+
#ff0000
+
text color (red)
+
noinherit
+
don't inherit styles from supertoken
+
+

Note that there may not be a space between bg: and the color value +since the style definition string is split at whitespace. +Also, using named colors is not allowed since the supported color names +vary for different formatters.

+

Furthermore, not all lexers might support every style.

+
+
+

Builtin Styles

+

Pygments ships some builtin styles which are maintained by the Pygments team.

+

To get a list of known styles you can use this snippet:

+
>>> from pygments.styles import STYLE_MAP
+>>> STYLE_MAP.keys()
+['default', 'emacs', 'friendly', 'colorful']
+
+
+
+

Getting a list of available styles

+

New in Pygments 0.6.

+

Because it could be that a plugin registered a style, there is +a way to iterate over all styles:

+
>>> from pygments.styles import get_all_styles
+>>> styles = list(get_all_styles())
+
+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/tokens.html b/docs/build/tokens.html new file mode 100644 index 0000000..b80aa1b --- /dev/null +++ b/docs/build/tokens.html @@ -0,0 +1,541 @@ + + + + Builtin Tokens — Pygments + + + + +
+

Pygments

+

Builtin Tokens

+ + « Back To Index + + +
+

Contents

+ +
+ + +

Inside the pygments.token module, there is a special object called Token +that is used to create token types.

+

You can create a new token type by accessing an attribute of Token:

+
>>> from pygments.token import Token
+>>> Token.String
+Token.String
+>>> Token.String is Token.String
+True
+
+

Note that tokens are singletons so you can use the is operator for comparing +token types.

+

As of Pygments 0.7 you can also use the in operator to perform set tests:

+
>>> from pygments.token import Comment
+>>> Comment.Single in Comment
+True
+>>> Comment in Comment.Multi
+False
+
+

This can be useful in filters and if you write lexers on your own without +using the base lexers.

+

You can also split a token type into a hierarchy, and get the parent of it:

+
>>> String.split()
+[Token, Token.Literal, Token.Literal.String]
+>>> String.parent
+Token.Literal
+
+

In principle, you can create an unlimited number of token types but nobody can +guarantee that a style would define style rules for a token type. Because of +that, Pygments proposes some global token types defined in the +pygments.token.STANDARD_TYPES dict.

+

For some tokens aliases are already defined:

+
>>> from pygments.token import String
+>>> String
+Token.Literal.String
+
+

Inside the pygments.token module the following aliases are defined:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TextToken.Textfor any type of text data
WhitespaceToken.Text.Whitespacefor specially highlighted whitespace
ErrorToken.Errorrepresents lexer errors
OtherToken.Otherspecial token for data not +matched by a parser (e.g. HTML +markup in PHP code)
KeywordToken.Keywordany kind of keywords
NameToken.Namevariable/function names
LiteralToken.LiteralAny literals
StringToken.Literal.Stringstring literals
NumberToken.Literal.Numbernumber literals
OperatorToken.Operatoroperators (+, not...)
PunctuationToken.Punctuationpunctuation ([, (...)
CommentToken.Commentany kind of comments
GenericToken.Genericgeneric tokens (have a look at +the explanation below)
+

The Whitespace token type is new in Pygments 0.8. It is used only by the +VisibleWhitespaceFilter currently.

+

Normally you just create token types using the already defined aliases. For each +of those token aliases, a number of subtypes exists (excluding the special tokens +Token.Text, Token.Error and Token.Other)

+

The is_token_subtype() function in the pygments.token module can be used to +test if a token type is a subtype of another (such as Name.Tag and Name). +(This is the same as Name.Tag in Name. The overloaded in operator was newly +introduced in Pygments 0.7, the function still exists for backwards +compatiblity.)

+

With Pygments 0.7, it's also possible to convert strings to token types (for example +if you want to supply a token from the command line):

+
>>> from pygments.token import String, string_to_tokentype
+>>> string_to_tokentype("String")
+Token.Literal.String
+>>> string_to_tokentype("Token.Literal.String")
+Token.Literal.String
+>>> string_to_tokentype(String)
+Token.Literal.String
+
+
+

Keyword Tokens

+
+
Keyword
+
For any kind of keyword (especially if it doesn't match any of the +subtypes of course).
+
Keyword.Constant
+
For keywords that are constants (e.g. None in future Python versions).
+
Keyword.Declaration
+
For keywords used for variable declaration (e.g. var in some programming +languages like JavaScript).
+
Keyword.Namespace
+
For keywords used for namespace declarations (e.g. import in Python and +Java and package in Java).
+
Keyword.Pseudo
+
For keywords that aren't really keywords (e.g. None in old Python +versions).
+
Keyword.Reserved
+
For reserved keywords.
+
Keyword.Type
+
For builtin types that can't be used as identifiers (e.g. int, +char etc. in C).
+
+
+
+

Name Tokens

+
+
Name
+
For any name (variable names, function names, classes).
+
Name.Attribute
+
For all attributes (e.g. in HTML tags).
+
Name.Builtin
+
Builtin names; names that are available in the global namespace.
+
Name.Builtin.Pseudo
+
Builtin names that are implicit (e.g. self in Ruby, this in Java).
+
Name.Class
+
Class names. Because no lexer can know if a name is a class or a function +or something else this token is meant for class declarations.
+
Name.Constant
+
Token type for constants. In some languages you can recognise a token by the +way it's defined (the value after a const keyword for example). In +other languages constants are uppercase by definition (Ruby).
+
Name.Decorator
+
Token type for decorators. Decorators are synatic elements in the Python +language. Similar syntax elements exist in C# and Java.
+
Name.Entity
+
Token type for special entities. (e.g. &nbsp; in HTML).
+
Name.Exception
+
Token type for exception names (e.g. RuntimeError in Python). Some languages +define exceptions in the function signature (Java). You can highlight +the name of that exception using this token then.
+
Name.Function
+
Token type for function names.
+
Name.Label
+
Token type for label names (e.g. in languages that support goto).
+
Name.Namespace
+
Token type for namespaces. (e.g. import paths in Java/Python), names following +the module/namespace keyword in other languages.
+
Name.Other
+
Other names. Normally unused.
+
Name.Tag
+
Tag names (in HTML/XML markup or configuration files).
+
Name.Variable
+
Token type for variables. Some languages have prefixes for variable names +(PHP, Ruby, Perl). You can highlight them using this token.
+
Name.Variable.Class
+
same as Name.Variable but for class variables (also static variables).
+
Name.Variable.Global
+
same as Name.Variable but for global variables (used in Ruby, for +example).
+
Name.Variable.Instance
+
same as Name.Variable but for instance variables.
+
+
+
+

Literals

+
+
Literal
+
For any literal (if not further defined).
+
Literal.Date
+
for date literals (e.g. 42d in Boo).
+
String
+
For any string literal.
+
String.Backtick
+
Token type for strings enclosed in backticks.
+
String.Char
+
Token type for single characters (e.g. Java, C).
+
String.Doc
+
Token type for documentation strings (for example Python).
+
String.Double
+
Double quoted strings.
+
String.Escape
+
Token type for escape sequences in strings.
+
String.Heredoc
+
Token type for "heredoc" strings (e.g. in Ruby or Perl).
+
String.Interpol
+
Token type for interpolated parts in strings (e.g. #{foo} in Ruby).
+
String.Other
+
Token type for any other strings (for example %q{foo} string constructs +in Ruby).
+
String.Regex
+
Token type for regular expression literals (e.g. /foo/ in JavaScript).
+
String.Single
+
Token type for single quoted strings.
+
String.Symbol
+
Token type for symbols (e.g. :foo in LISP or Ruby).
+
Number
+
Token type for any number literal.
+
Number.Float
+
Token type for float literals (e.g. 42.0).
+
Number.Hex
+
Token type for hexadecimal number literals (e.g. 0xdeadbeef).
+
Number.Integer
+
Token type for integer literals (e.g. 42).
+
Number.Integer.Long
+
Token type for long integer literals (e.g. 42L in Python).
+
Number.Oct
+
Token type for octal literals.
+
+
+
+

Operators

+
+
Operator
+
For any punctuation operator (e.g. +, -).
+
Operator.Word
+
For any operator that is a word (e.g. not).
+
+
+
+

Punctuation

+

New in Pygments 0.7.

+
+
Punctuation
+
For any punctuation which is not an operator (e.g. [, (...)
+
+
+
+

Comments

+
+
Comment
+
Token type for any comment.
+
Comment.Multiline
+
Token type for multiline comments.
+
Comment.Preproc
+
Token type for preprocessor comments (also <?php/<% constructs).
+
Comment.Single
+
Token type for comments that end at the end of a line (e.g. # foo).
+
Comment.Special
+
Special data in comments. For example code tags, author and license +informations etc.
+
+
+
+

Generic Tokens

+

Generic tokens are for special lexers like the DiffLexer that doesn't really +highlight a programming language but a patch file.

+
+
Generic
+
A generic, unstyled token. Normally you don't use this token type.
+
Generic.Deleted
+
Marks the token value as deleted.
+
Generic.Emph
+
Marks the token value as emphasized.
+
Generic.Error
+
Marks the token value as an error message.
+
Generic.Heading
+
Marks the token value as headline.
+
Generic.Inserted
+
Marks the token value as inserted.
+
Generic.Output
+
Marks the token value as program output (e.g. for python cli lexer).
+
Generic.Prompt
+
Marks the token value as command prompt (e.g. bash lexer).
+
Generic.Strong
+
Marks the token value as bold (e.g. for rst lexer).
+
Generic.Subheading
+
Marks the token value as subheadline.
+
Generic.Traceback
+
Marks the token value as a part of an error traceback.
+
+
+ +
+ + + \ No newline at end of file diff --git a/docs/build/unicode.html b/docs/build/unicode.html new file mode 100644 index 0000000..768495e --- /dev/null +++ b/docs/build/unicode.html @@ -0,0 +1,249 @@ + + + + Unicode and Encodings — Pygments + + + + +
+

Pygments

+

Unicode and Encodings

+ + « Back To Index + + +

Since Pygments 0.6, all lexers use unicode strings internally. Because of that +you might encounter the occasional UnicodeDecodeError if you pass strings with the +wrong encoding.

+

Per default all lexers have their input encoding set to latin1. +If you pass a lexer a string object (not unicode), it tries to decode the data +using this encoding. +You can override the encoding using the encoding lexer option. If you have the +chardet library installed and set the encoding to chardet if will ananlyse +the text and use the encoding it thinks is the right one automatically:

+
from pygments.lexers import PythonLexer
+lexer = PythonLexer(encoding='chardet')
+
+

The best way is to pass Pygments unicode objects. In that case you can't get +unexpected output.

+

The formatters now send Unicode objects to the stream if you don't set the +output encoding. You can do so by passing the formatters an encoding option:

+
from pygments.formatters import HtmlFormatter
+f = HtmlFormatter(encoding='utf-8')
+
+

You will have to set this option if you have non-ASCII characters in the +source and the output stream does not accept Unicode written to it! +This is the case for all regular files and for terminals.

+

Note: The Terminal formatter tries to be smart: if its output stream has an +encoding attribute, and you haven't set the option, it will encode any +Unicode string with this encoding before writing it. This is the case for +sys.stdout, for example. The other formatters don't have that behavior.

+

Another note: If you call Pygments via the command line (pygmentize), +encoding is handled differently, see the command line docs.

+

New in Pygments 0.7: the formatters now also accept an outencoding option +which will override the encoding option if given. This makes it possible to +use a single options dict with lexers and formatters, and still have different +input and output encodings.

+ +
+ + + \ No newline at end of file diff --git a/docs/generate.py b/docs/generate.py new file mode 100644 index 0000000..436b79e --- /dev/null +++ b/docs/generate.py @@ -0,0 +1,472 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Generate Pygments Documentation + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Generates a bunch of html files containing the documentation. + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import sys +from datetime import datetime +from cgi import escape + +from docutils import nodes +from docutils.parsers.rst import directives +from docutils.core import publish_parts +from docutils.writers import html4css1 + +from jinja2 import Template + +# try to use the right Pygments to build the docs +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from pygments import highlight, __version__ +from pygments.lexers import get_lexer_by_name +from pygments.formatters import HtmlFormatter + + +LEXERDOC = ''' +`%s` +%s + :Short names: %s + :Filename patterns: %s + :Mimetypes: %s + +''' + +def generate_lexer_docs(): + from pygments.lexers import LEXERS + + out = [] + + modules = {} + moduledocstrings = {} + for classname, data in sorted(LEXERS.iteritems(), key=lambda x: x[0]): + module = data[0] + mod = __import__(module, None, None, [classname]) + cls = getattr(mod, classname) + if not cls.__doc__: + print "Warning: %s does not have a docstring." % classname + modules.setdefault(module, []).append(( + classname, + cls.__doc__, + ', '.join(data[2]) or 'None', + ', '.join(data[3]).replace('*', '\\*') or 'None', + ', '.join(data[4]) or 'None')) + if module not in moduledocstrings: + moduledocstrings[module] = mod.__doc__ + + for module, lexers in sorted(modules.iteritems(), key=lambda x: x[0]): + heading = moduledocstrings[module].splitlines()[4].strip().rstrip('.') + out.append('\n' + heading + '\n' + '-'*len(heading) + '\n') + for data in lexers: + out.append(LEXERDOC % data) + return ''.join(out).decode('utf-8') + +def generate_formatter_docs(): + from pygments.formatters import FORMATTERS + + out = [] + for cls, data in sorted(FORMATTERS.iteritems(), + key=lambda x: x[0].__name__): + heading = cls.__name__ + out.append('`' + heading + '`\n' + '-'*(2+len(heading)) + '\n') + out.append(cls.__doc__) + out.append(''' + :Short names: %s + :Filename patterns: %s + + +''' % (', '.join(data[1]) or 'None', ', '.join(data[2]).replace('*', '\\*') or 'None')) + return ''.join(out).decode('utf-8') + +def generate_filter_docs(): + from pygments.filters import FILTERS + + out = [] + for name, cls in FILTERS.iteritems(): + out.append(''' +`%s` +%s + :Name: %s +''' % (cls.__name__, cls.__doc__, name)) + return ''.join(out).decode('utf-8') + +def generate_changelog(): + fn = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', + 'CHANGES')) + f = file(fn) + result = [] + in_header = False + header = True + for line in f: + if header: + if not in_header and line.strip(): + in_header = True + elif in_header and not line.strip(): + header = False + else: + result.append(line.rstrip()) + f.close() + return '\n'.join(result).decode('utf-8') + +def generate_authors(): + fn = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', + 'AUTHORS')) + f = file(fn) + r = f.read().rstrip() + f.close() + return r + +LEXERDOCS = generate_lexer_docs() +FORMATTERDOCS = generate_formatter_docs() +FILTERDOCS = generate_filter_docs() +CHANGELOG = generate_changelog() +AUTHORS = generate_authors() + + +PYGMENTS_FORMATTER = HtmlFormatter(style='pastie', cssclass='syntax') + +USAGE = '''\ +Usage: %s [ ...] + +Generate either python or html files out of the documentation. + +Mode can either be python or html.\ +''' % sys.argv[0] + +TEMPLATE = '''\ + + + + {{ title }} — Pygments + + + + +
+

Pygments

+

{{ title }}

+ {% if file_id != "index" %} + « Back To Index + {% endif %} + {% if toc %} +
+

Contents

+
    + {% for key, value in toc %} +
  • {{ value }}
  • + {% endfor %} +
+
+ {% endif %} + {{ body }} +
+ + +\ +''' + +STYLESHEET = '''\ +body { + background-color: #f2f2f2; + margin: 0; + padding: 0; + font-family: 'Georgia', serif; + color: #111; +} + +#content { + background-color: white; + padding: 20px; + margin: 20px auto 20px auto; + max-width: 800px; + border: 4px solid #ddd; +} + +h1 { + font-weight: normal; + font-size: 40px; + color: #09839A; +} + +h2 { + font-weight: normal; + font-size: 30px; + color: #C73F00; +} + +h1.heading { + margin: 0 0 30px 0; +} + +h2.subheading { + margin: -30px 0 0 45px; +} + +h3 { + margin-top: 30px; +} + +table.docutils { + border-collapse: collapse; + border: 2px solid #aaa; + margin: 0.5em 1.5em 0.5em 1.5em; +} + +table.docutils td { + padding: 2px; + border: 1px solid #ddd; +} + +p, li, dd, dt, blockquote { + font-size: 15px; + color: #333; +} + +p { + line-height: 150%; + margin-bottom: 0; + margin-top: 10px; +} + +hr { + border-top: 1px solid #ccc; + border-bottom: 0; + border-right: 0; + border-left: 0; + margin-bottom: 10px; + margin-top: 20px; +} + +dl { + margin-left: 10px; +} + +li, dt { + margin-top: 5px; +} + +dt { + font-weight: bold; +} + +th { + text-align: left; +} + +a { + color: #990000; +} + +a:hover { + color: #c73f00; +} + +pre { + background-color: #f9f9f9; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; + padding: 5px; + font-size: 13px; + font-family: Bitstream Vera Sans Mono,monospace; +} + +tt { + font-size: 13px; + font-family: Bitstream Vera Sans Mono,monospace; + color: black; + padding: 1px 2px 1px 2px; + background-color: #f0f0f0; +} + +cite { + /* abusing , it's generated by ReST for `x` */ + font-size: 13px; + font-family: Bitstream Vera Sans Mono,monospace; + font-weight: bold; + font-style: normal; +} + +#backlink { + float: right; + font-size: 11px; + color: #888; +} + +div.toc { + margin: 0 0 10px 0; +} + +div.toc h2 { + font-size: 20px; +} +''' #' + + +def pygments_directive(name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + try: + lexer = get_lexer_by_name(arguments[0]) + except ValueError: + # no lexer found + lexer = get_lexer_by_name('text') + parsed = highlight(u'\n'.join(content), lexer, PYGMENTS_FORMATTER) + return [nodes.raw('', parsed, format="html")] +pygments_directive.arguments = (1, 0, 1) +pygments_directive.content = 1 +directives.register_directive('sourcecode', pygments_directive) + + +def create_translator(link_style): + class Translator(html4css1.HTMLTranslator): + def visit_reference(self, node): + refuri = node.get('refuri') + if refuri is not None and '/' not in refuri and refuri.endswith('.txt'): + node['refuri'] = link_style(refuri[:-4]) + html4css1.HTMLTranslator.visit_reference(self, node) + return Translator + + +class DocumentationWriter(html4css1.Writer): + + def __init__(self, link_style): + html4css1.Writer.__init__(self) + self.translator_class = create_translator(link_style) + + def translate(self): + html4css1.Writer.translate(self) + # generate table of contents + contents = self.build_contents(self.document) + contents_doc = self.document.copy() + contents_doc.children = contents + contents_visitor = self.translator_class(contents_doc) + contents_doc.walkabout(contents_visitor) + self.parts['toc'] = self._generated_toc + + def build_contents(self, node, level=0): + sections = [] + i = len(node) - 1 + while i >= 0 and isinstance(node[i], nodes.section): + sections.append(node[i]) + i -= 1 + sections.reverse() + toc = [] + for section in sections: + try: + reference = nodes.reference('', '', refid=section['ids'][0], *section[0]) + except IndexError: + continue + ref_id = reference['refid'] + text = escape(reference.astext()) + toc.append((ref_id, text)) + + self._generated_toc = [('#%s' % href, caption) for href, caption in toc] + # no further processing + return [] + + +def generate_documentation(data, link_style): + writer = DocumentationWriter(link_style) + data = data.replace('[builtin_lexer_docs]', LEXERDOCS).\ + replace('[builtin_formatter_docs]', FORMATTERDOCS).\ + replace('[builtin_filter_docs]', FILTERDOCS).\ + replace('[changelog]', CHANGELOG).\ + replace('[authors]', AUTHORS) + parts = publish_parts( + data, + writer=writer, + settings_overrides={ + 'initial_header_level': 3, + 'field_name_limit': 50, + } + ) + return { + 'title': parts['title'], + 'body': parts['body'], + 'toc': parts['toc'] + } + + +def handle_python(filename, fp, dst): + now = datetime.now() + title = os.path.basename(filename)[:-4] + content = fp.read() + def urlize(href): + # create links for the pygments webpage + if href == 'index.txt': + return '/docs/' + else: + return '/docs/%s/' % href + parts = generate_documentation(content, urlize) + result = file(os.path.join(dst, title + '.py'), 'w') + result.write('# -*- coding: utf-8 -*-\n') + result.write('"""\n Pygments Documentation - %s\n' % title) + result.write(' %s\n\n' % ('~' * (24 + len(title)))) + result.write(' Generated on: %s\n"""\n\n' % now) + result.write('import datetime\n') + result.write('DATE = %r\n' % now) + result.write('TITLE = %r\n' % parts['title']) + result.write('TOC = %r\n' % parts['toc']) + result.write('BODY = %r\n' % parts['body']) + result.close() + + +def handle_html(filename, fp, dst): + now = datetime.now() + title = os.path.basename(filename)[:-4] + content = fp.read().decode('utf-8') + c = generate_documentation(content, (lambda x: './%s.html' % x)) + result = file(os.path.join(dst, title + '.html'), 'w') + c['style'] = STYLESHEET + PYGMENTS_FORMATTER.get_style_defs('.syntax') + c['generation_date'] = now + c['file_id'] = title + t = Template(TEMPLATE) + result.write(t.render(c).encode('utf-8')) + result.close() + + +def run(handle_file, dst, sources=()): + path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src')) + if not sources: + sources = [os.path.join(path, fn) for fn in os.listdir(path)] + if not os.path.isdir(dst): + os.makedirs(dst) + print 'Making docs for Pygments %s in %s' % (__version__, dst) + for fn in sources: + if not os.path.isfile(fn): + continue + print 'Processing %s' % fn + f = open(fn) + try: + handle_file(fn, f, dst) + finally: + f.close() + + +def main(mode, dst='build/', *sources): + try: + handler = { + 'html': handle_html, + 'python': handle_python + }[mode] + except KeyError: + print 'Error: unknown mode "%s"' % mode + sys.exit(1) + run(handler, os.path.realpath(dst), sources) + + +if __name__ == '__main__': + if len(sys.argv) == 1: + print USAGE + else: + main(*sys.argv[1:]) diff --git a/docs/pygmentize.1 b/docs/pygmentize.1 new file mode 100644 index 0000000..71bb6f9 --- /dev/null +++ b/docs/pygmentize.1 @@ -0,0 +1,94 @@ +.TH PYGMENTIZE 1 "February 15, 2007" + +.SH NAME +pygmentize \- highlights the input file + +.SH SYNOPSIS +.B \fBpygmentize\fP +.RI [-l\ \fI\fP]\ [-F\ \fI\fP[:\fI\fP]]\ [-f\ \fI\fP] +.RI [-O\ \fI\fP]\ [-P\ \fI\fP]\ [-o\ \fI\fP]\ [\fI\fP] +.br +.B \fBpygmentize\fP +.RI -S\ \fI + + +

%(title)s

+ +''' + +DOC_HEADER_EXTERNALCSS = '''\ + + + + + %(title)s + + + + +

%(title)s

+ +''' + +DOC_FOOTER = '''\ + + +''' + + +class HtmlFormatter(Formatter): + r""" + Format tokens as HTML 4 ```` tags within a ``
`` tag, wrapped
+    in a ``
`` tag. The ``
``'s CSS class can be set by the `cssclass` + option. + + If the `linenos` option is set to ``"table"``, the ``
`` is
+    additionally wrapped inside a ```` which has one row and two
+    cells: one containing the line numbers and one containing the code.
+    Example:
+
+    .. sourcecode:: html
+
+        
+
+ + +
+
1
+            2
+
+
def foo(bar):
+              pass
+            
+
+ + (whitespace added to improve clarity). + + Wrapping can be disabled using the `nowrap` option. + + A list of lines can be specified using the `hl_lines` option to make these + lines highlighted (as of Pygments 0.11). + + With the `full` option, a complete HTML 4 document is output, including + the style definitions inside a `` + + +

Code tags report for %s

+ + +%s +
LineTagWhoDescription
+ + +''' + + TABLE = '\nFile: %s\n' + + TR = ('%%(lno)d' + '%%(tag)s' + '%%(who)s%%(what)s') + + f = file(output, 'w') + table = '\n'.join(TABLE % fname + + '\n'.join(TR % (no % 2,) % entry + for no, entry in enumerate(store[fname])) + for fname in sorted(store)) + f.write(HTML % (', '.join(map(abspath, args)), table)) + f.close() + + print "Report written to %s." % output + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/find_error.py b/scripts/find_error.py new file mode 100644 index 0000000..44dd053 --- /dev/null +++ b/scripts/find_error.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" + Lexing error finder + ~~~~~~~~~~~~~~~~~~~ + + For the source files given on the command line, display + the text where Error tokens are being generated, along + with some context. + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import sys, os + +try: + import pygments +except ImportError: + # try parent path + sys.path.append(os.path.join(os.path.dirname(__file__), "..")) + +from pygments import highlight +from pygments.lexers import get_lexer_for_filename, get_lexer_by_name +from pygments.token import Error + +def main(fn): + try: + lx = get_lexer_for_filename(fn) + except ValueError: + try: + name, rest = fn.split("_", 1) + lx = get_lexer_by_name(name) + except ValueError: + raise AssertionError('no lexer found for file %r' % fn) + text = file(fn, 'U').read() + text = text.strip('\n') + '\n' + text = text.decode('latin1') + ntext = [] + for type, val in lx.get_tokens(text): + if type == Error: + print "Error parsing", fn + print "\n".join([' ' + repr(x) for x in ntext[-num:]]) + print `val` + "<<<" + return 1 + ntext.append((type,val)) + return 0 + + +num = 10 + +if __name__ == "__main__": + if sys.argv[1][:2] == '-n': + num = int(sys.argv[1][2:]) + del sys.argv[1] + ret = 0 + for f in sys.argv[1:]: + ret += main(f) + sys.exit(bool(ret)) diff --git a/scripts/get_vimkw.py b/scripts/get_vimkw.py new file mode 100644 index 0000000..1ecf714 --- /dev/null +++ b/scripts/get_vimkw.py @@ -0,0 +1,38 @@ +import re +from pprint import pprint + +r_line = re.compile(r"^(syn keyword vimCommand contained|syn keyword vimOption " + r"contained|syn keyword vimAutoEvent contained)\s+(.*)") +r_item = re.compile(r"(\w+)(?:\[(\w+)\])?") + +def getkw(input, output): + out = file(output, 'w') + + output_info = {'command': [], 'option': [], 'auto': []} + for line in file(input): + m = r_line.match(line) + if m: + # Decide which output gets mapped to d + if 'vimCommand' in m.group(1): + d = output_info['command'] + elif 'AutoEvent' in m.group(1): + d = output_info['auto'] + else: + d = output_info['option'] + + # Extract all the shortened versions + for i in r_item.finditer(m.group(2)): + d.append((i.group(1), "%s%s" % (i.group(1), i.group(2) or ''))) + d.sort() + + for a, b in output_info.items(): + print >>out, '%s=%r' % (a, b) + +def is_keyword(w, keywords): + for i in range(len(w), 0, -1): + if w[:i] in keywords: + return signals[w[:i]][:len(w)] == w + return False + +if __name__ == "__main__": + getkw("/usr/share/vim/vim70/syntax/vim.vim", "temp.py") diff --git a/scripts/pylintrc b/scripts/pylintrc new file mode 100644 index 0000000..aa04e12 --- /dev/null +++ b/scripts/pylintrc @@ -0,0 +1,301 @@ +# lint Python modules using external checkers. +# +# This is the main checker controling the other ones and the reports +# generation. It is itself both a raw checker and an astng checker in order +# to: +# * handle message activation / deactivation at the module level +# * handle some basic but necessary stats'data (number of classes, methods...) +# +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Profiled execution. +profile=no + +# Add to the black list. It should be a base name, not a +# path. You may set this option multiple times. +ignore=.svn + +# Pickle collected data for later comparisons. +persistent=yes + +# Set the cache size for astng objects. +cache-size=500 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[MESSAGES CONTROL] + +# Enable only checker(s) with the given id(s). This option conflict with the +# disable-checker option +#enable-checker= + +# Enable all checker(s) except those with the given id(s). This option conflict +# with the disable-checker option +#disable-checker= + +# Enable all messages in the listed categories. +#enable-msg-cat= + +# Disable all messages in the listed categories. +#disable-msg-cat= + +# Enable the message(s) with the given id(s). +#enable-msg= + +# Disable the message(s) with the given id(s). +disable-msg=C0323,W0142,C0301,C0103,C0111,E0213,C0302,C0203,W0703,R0201 + + +[REPORTS] + +# set the output format. Available formats are text, parseable, colorized and +# html +output-format=colorized + +# Include message's id in output +include-ids=yes + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells wether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note).You have access to the variables errors warning, statement which +# respectivly contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (R0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (R0004). +comment=no + +# Enable the report(s) with the given id(s). +#enable-report= + +# Disable the report(s) with the given id(s). +#disable-report= + + +# checks for +# * unused variables / imports +# * undefined variables +# * redefinition of variable from builtins or from an outer scope +# * use of variable before assigment +# +[VARIABLES] + +# Tells wether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching names used for dummy variables (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +# try to find bugs in the code using type inference +# +[TYPECHECK] + +# Tells wether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# When zope mode is activated, consider the acquired-members option to ignore +# access to some undefined attributes. +zope=no + +# List of members which are usually get through zope's acquisition mecanism and +# so shouldn't trigger E0201 when accessed (need zope=yes to be considered). +acquired-members=REQUEST,acl_users,aq_parent + + +# checks for : +# * doc strings +# * modules / classes / functions / methods / arguments / variables name +# * number of arguments, local variables, branchs, returns and statements in +# functions, methods +# * required module attributes +# * dangerous default values as arguments +# * redefinition of function / method / class +# * uses of the global statement +# +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes= + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# List of builtins function names that should not be used, separated by a comma +bad-functions=apply,input + + +# checks for sign of poor/misdesign: +# * number of methods, attributes, local variables... +# * size, complexity of functions, methods +# +[DESIGN] + +# Maximum number of arguments for function / method +max-args=12 + +# Maximum number of locals for function / method body +max-locals=30 + +# Maximum number of return / yield for function / method body +max-returns=12 + +# Maximum number of branch for function / method body +max-branchs=30 + +# Maximum number of statements in function / method body +max-statements=60 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=20 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=0 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +# checks for +# * external modules dependencies +# * relative / wildcard imports +# * cyclic imports +# * uses of deprecated modules +# +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report R0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report R0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report R0402 must +# not be disabled) +int-import-graph= + + +# checks for : +# * methods without self as first argument +# * overridden methods signature +# * access only to existant members via self +# * attributes not defined in the __init__ method +# * supported interfaces implementation +# * unreachable code +# +[CLASSES] + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + + +# checks for similarities and duplicated code. This computation may be +# memory / CPU intensive, so you should disable it if you experiments some +# problems. +# +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=10 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +# checks for: +# * warning notes in the code like FIXME, XXX +# * PEP 263: source code with non ascii character but no encoding declaration +# +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +# checks for : +# * unauthorized constructions +# * strict indentation +# * line length +# * use of <> instead of != +# +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=90 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' diff --git a/scripts/reindent.py b/scripts/reindent.py new file mode 100755 index 0000000..e6ee828 --- /dev/null +++ b/scripts/reindent.py @@ -0,0 +1,291 @@ +#! /usr/bin/env python + +# Released to the public domain, by Tim Peters, 03 October 2000. +# -B option added by Georg Brandl, 2006. + +"""reindent [-d][-r][-v] [ path ... ] + +-d (--dryrun) Dry run. Analyze, but don't make any changes to files. +-r (--recurse) Recurse. Search for all .py files in subdirectories too. +-B (--no-backup) Don't write .bak backup files. +-v (--verbose) Verbose. Print informative msgs; else only names of changed files. +-h (--help) Help. Print this usage information and exit. + +Change Python (.py) files to use 4-space indents and no hard tab characters. +Also trim excess spaces and tabs from ends of lines, and remove empty lines +at the end of files. Also ensure the last line ends with a newline. + +If no paths are given on the command line, reindent operates as a filter, +reading a single source file from standard input and writing the transformed +source to standard output. In this case, the -d, -r and -v flags are +ignored. + +You can pass one or more file and/or directory paths. When a directory +path, all .py files within the directory will be examined, and, if the -r +option is given, likewise recursively for subdirectories. + +If output is not to standard output, reindent overwrites files in place, +renaming the originals with a .bak extension. If it finds nothing to +change, the file is left alone. If reindent does change a file, the changed +file is a fixed-point for future runs (i.e., running reindent on the +resulting .py file won't change it again). + +The hard part of reindenting is figuring out what to do with comment +lines. So long as the input files get a clean bill of health from +tabnanny.py, reindent should do a good job. +""" + +__version__ = "1" + +import tokenize +import os +import sys + +verbose = 0 +recurse = 0 +dryrun = 0 +no_backup = 0 + +def usage(msg=None): + if msg is not None: + print >> sys.stderr, msg + print >> sys.stderr, __doc__ + +def errprint(*args): + sep = "" + for arg in args: + sys.stderr.write(sep + str(arg)) + sep = " " + sys.stderr.write("\n") + +def main(): + import getopt + global verbose, recurse, dryrun, no_backup + + try: + opts, args = getopt.getopt(sys.argv[1:], "drvhB", + ["dryrun", "recurse", "verbose", "help", + "no-backup"]) + except getopt.error, msg: + usage(msg) + return + for o, a in opts: + if o in ('-d', '--dryrun'): + dryrun += 1 + elif o in ('-r', '--recurse'): + recurse += 1 + elif o in ('-v', '--verbose'): + verbose += 1 + elif o in ('-B', '--no-backup'): + no_backup += 1 + elif o in ('-h', '--help'): + usage() + return + if not args: + r = Reindenter(sys.stdin) + r.run() + r.write(sys.stdout) + return + for arg in args: + check(arg) + +def check(file): + if os.path.isdir(file) and not os.path.islink(file): + if verbose: + print "listing directory", file + names = os.listdir(file) + for name in names: + fullname = os.path.join(file, name) + if ((recurse and os.path.isdir(fullname) and + not os.path.islink(fullname)) + or name.lower().endswith(".py")): + check(fullname) + return + + if verbose: + print "checking", file, "...", + try: + f = open(file) + except IOError, msg: + errprint("%s: I/O Error: %s" % (file, str(msg))) + return + + r = Reindenter(f) + f.close() + if r.run(): + if verbose: + print "changed." + if dryrun: + print "But this is a dry run, so leaving it alone." + else: + print "reindented", file, (dryrun and "(dry run => not really)" or "") + if not dryrun: + if not no_backup: + bak = file + ".bak" + if os.path.exists(bak): + os.remove(bak) + os.rename(file, bak) + if verbose: + print "renamed", file, "to", bak + f = open(file, "w") + r.write(f) + f.close() + if verbose: + print "wrote new", file + else: + if verbose: + print "unchanged." + + +class Reindenter: + + def __init__(self, f): + self.find_stmt = 1 # next token begins a fresh stmt? + self.level = 0 # current indent level + + # Raw file lines. + self.raw = f.readlines() + + # File lines, rstripped & tab-expanded. Dummy at start is so + # that we can use tokenize's 1-based line numbering easily. + # Note that a line is all-blank iff it's "\n". + self.lines = [line.rstrip('\n \t').expandtabs() + "\n" + for line in self.raw] + self.lines.insert(0, None) + self.index = 1 # index into self.lines of next line + + # List of (lineno, indentlevel) pairs, one for each stmt and + # comment line. indentlevel is -1 for comment lines, as a + # signal that tokenize doesn't know what to do about them; + # indeed, they're our headache! + self.stats = [] + + def run(self): + tokenize.tokenize(self.getline, self.tokeneater) + # Remove trailing empty lines. + lines = self.lines + while lines and lines[-1] == "\n": + lines.pop() + # Sentinel. + stats = self.stats + stats.append((len(lines), 0)) + # Map count of leading spaces to # we want. + have2want = {} + # Program after transformation. + after = self.after = [] + # Copy over initial empty lines -- there's nothing to do until + # we see a line with *something* on it. + i = stats[0][0] + after.extend(lines[1:i]) + for i in range(len(stats)-1): + thisstmt, thislevel = stats[i] + nextstmt = stats[i+1][0] + have = getlspace(lines[thisstmt]) + want = thislevel * 4 + if want < 0: + # A comment line. + if have: + # An indented comment line. If we saw the same + # indentation before, reuse what it most recently + # mapped to. + want = have2want.get(have, -1) + if want < 0: + # Then it probably belongs to the next real stmt. + for j in xrange(i+1, len(stats)-1): + jline, jlevel = stats[j] + if jlevel >= 0: + if have == getlspace(lines[jline]): + want = jlevel * 4 + break + if want < 0: # Maybe it's a hanging + # comment like this one, + # in which case we should shift it like its base + # line got shifted. + for j in xrange(i-1, -1, -1): + jline, jlevel = stats[j] + if jlevel >= 0: + want = have + getlspace(after[jline-1]) - \ + getlspace(lines[jline]) + break + if want < 0: + # Still no luck -- leave it alone. + want = have + else: + want = 0 + assert want >= 0 + have2want[have] = want + diff = want - have + if diff == 0 or have == 0: + after.extend(lines[thisstmt:nextstmt]) + else: + for line in lines[thisstmt:nextstmt]: + if diff > 0: + if line == "\n": + after.append(line) + else: + after.append(" " * diff + line) + else: + remove = min(getlspace(line), -diff) + after.append(line[remove:]) + return self.raw != self.after + + def write(self, f): + f.writelines(self.after) + + # Line-getter for tokenize. + def getline(self): + if self.index >= len(self.lines): + line = "" + else: + line = self.lines[self.index] + self.index += 1 + return line + + # Line-eater for tokenize. + def tokeneater(self, type, token, (sline, scol), end, line, + INDENT=tokenize.INDENT, + DEDENT=tokenize.DEDENT, + NEWLINE=tokenize.NEWLINE, + COMMENT=tokenize.COMMENT, + NL=tokenize.NL): + + if type == NEWLINE: + # A program statement, or ENDMARKER, will eventually follow, + # after some (possibly empty) run of tokens of the form + # (NL | COMMENT)* (INDENT | DEDENT+)? + self.find_stmt = 1 + + elif type == INDENT: + self.find_stmt = 1 + self.level += 1 + + elif type == DEDENT: + self.find_stmt = 1 + self.level -= 1 + + elif type == COMMENT: + if self.find_stmt: + self.stats.append((sline, -1)) + # but we're still looking for a new stmt, so leave + # find_stmt alone + + elif type == NL: + pass + + elif self.find_stmt: + # This is the first "real token" following a NEWLINE, so it + # must be the first token of the next program statement, or an + # ENDMARKER. + self.find_stmt = 0 + if line: # not endmarker + self.stats.append((sline, self.level)) + +# Count number of leading blanks. +def getlspace(line): + i, n = 0, len(line) + while i < n and line[i] == " ": + i += 1 + return i + +if __name__ == '__main__': + main() diff --git a/scripts/vim2pygments.py b/scripts/vim2pygments.py new file mode 100644 index 0000000..1acac14 --- /dev/null +++ b/scripts/vim2pygments.py @@ -0,0 +1,933 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Vim Colorscheme Converter + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + This script converts vim colorscheme files to valid pygments + style classes meant for putting into modules. + + :copyright 2006 by Armin Ronacher. + :license: BSD, see LICENSE for details. +""" + +import sys +import re +from os import path +from cStringIO import StringIO + +split_re = re.compile(r'(? 2 and \ + len(parts[0]) >= 2 and \ + 'highlight'.startswith(parts[0]): + token = parts[1].lower() + if token not in TOKENS: + continue + for item in parts[2:]: + p = item.split('=', 1) + if not len(p) == 2: + continue + key, value = p + if key in ('ctermfg', 'guifg'): + color = get_vim_color(value) + if color: + set('color', color) + elif key in ('ctermbg', 'guibg'): + color = get_vim_color(value) + if color: + set('bgcolor', color) + elif key in ('term', 'cterm', 'gui'): + items = value.split(',') + for item in items: + item = item.lower() + if item == 'none': + set('noinherit', True) + elif item == 'bold': + set('bold', True) + elif item == 'underline': + set('underline', True) + elif item == 'italic': + set('italic', True) + + if bg_color is not None and not colors['Normal'].get('bgcolor'): + colors['Normal']['bgcolor'] = bg_color + + color_map = {} + for token, styles in colors.iteritems(): + if token in TOKENS: + tmp = [] + if styles.get('noinherit'): + tmp.append('noinherit') + if 'color' in styles: + tmp.append(styles['color']) + if 'bgcolor' in styles: + tmp.append('bg:' + styles['bgcolor']) + if styles.get('bold'): + tmp.append('bold') + if styles.get('italic'): + tmp.append('italic') + if styles.get('underline'): + tmp.append('underline') + tokens = TOKENS[token] + if not isinstance(tokens, tuple): + tokens = (tokens,) + for token in tokens: + color_map[token] = ' '.join(tmp) + + default_token = color_map.pop('') + return default_token, color_map + + +class StyleWriter(object): + + def __init__(self, code, name): + self.code = code + self.name = name.lower() + + def write_header(self, out): + out.write('# -*- coding: utf-8 -*-\n"""\n') + out.write(' %s Colorscheme\n' % self.name.title()) + out.write(' %s\n\n' % ('~' * (len(self.name) + 12))) + out.write(' Converted by %s\n' % SCRIPT_NAME) + out.write('"""\nfrom pygments.style import Style\n') + out.write('from pygments.token import Token, %s\n\n' % ', '.join(TOKEN_TYPES)) + out.write('class %sStyle(Style):\n\n' % self.name.title()) + + def write(self, out): + self.write_header(out) + default_token, tokens = find_colors(self.code) + tokens = tokens.items() + tokens.sort(lambda a, b: cmp(len(a[0]), len(a[1]))) + bg_color = [x[3:] for x in default_token.split() if x.startswith('bg:')] + if bg_color: + out.write(' background_color = %r\n' % bg_color[0]) + out.write(' styles = {\n') + out.write(' %-20s%r\n' % ('Token:', default_token)) + for token, definition in tokens: + if definition: + out.write(' %-20s%r\n' % (token + ':', definition)) + out.write(' }') + + def __repr__(self): + out = StringIO() + self.write_style(out) + return out.getvalue() + + +def convert(filename, stream=None): + name = path.basename(filename) + if name.endswith('.vim'): + name = name[:-4] + f = file(filename) + code = f.read() + f.close() + writer = StyleWriter(code, name) + if stream is not None: + out = stream + else: + out = StringIO() + writer.write(out) + if stream is None: + return out.getvalue() + + +def main(): + if len(sys.argv) != 2 or sys.argv[1] in ('-h', '--help'): + print 'Usage: %s ' % sys.argv[0] + return 2 + if sys.argv[1] in ('-v', '--version'): + print '%s %s' % (SCRIPT_NAME, SCRIPT_VERSION) + return + filename = sys.argv[1] + if not (path.exists(filename) and path.isfile(filename)): + print 'Error: %s not found' % filename + return 1 + convert(filename, sys.stdout) + sys.stdout.write('\n') + + +if __name__ == '__main__': + sys.exit(main() or 0) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..3581741 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,8 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + +[aliases] +release = egg_info -RDb '' + diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..b54823e --- /dev/null +++ b/setup.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Pygments + ~~~~~~~~ + + Pygments is a syntax highlighting package written in Python. + + It is a generic syntax highlighter for general use in all kinds of software + such as forum systems, wikis or other applications that need to prettify + source code. Highlights are: + + * a wide range of common languages and markup formats is supported + * special attention is paid to details, increasing quality by a fair amount + * support for new languages and formats are added easily + * a number of output formats, presently HTML, LaTeX, RTF, SVG and ANSI sequences + * it is usable as a command-line tool and as a library + * ... and it highlights even Brainfuck! + + The `Pygments tip`_ is installable with ``easy_install Pygments==dev``. + + .. _Pygments tip: + http://dev.pocoo.org/hg/pygments-main/archive/tip.tar.gz#egg=Pygments-dev + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +try: + from setuptools import setup, find_packages +except ImportError: + from distutils.core import setup + def find_packages(): + return [ + 'pygments', + 'pygments.lexers', + 'pygments.formatters', + 'pygments.styles', + 'pygments.filters', + ] + +try: + from distutils.command.build_py import build_py_2to3 as build_py +except ImportError: + from distutils.command.build_py import build_py + +setup( + name = 'Pygments', + version = '1.1.1', + url = 'http://pygments.org/', + license = 'BSD License', + author = 'Georg Brandl', + author_email = 'georg@python.org', + description = 'Pygments is a syntax highlighting package written in Python.', + long_description = __doc__, + keywords = 'syntax highlighting', + packages = find_packages(), + scripts = ['pygmentize'], + platforms = 'any', + zip_safe = False, + include_package_data = True, + classifiers = [ + 'License :: OSI Approved :: BSD License', + 'Intended Audience :: Developers', + 'Intended Audience :: End Users/Desktop', + 'Intended Audience :: System Administrators', + 'Development Status :: 5 - Production/Stable', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 3', + 'Operating System :: OS Independent', + ], + cmdclass = {'build_py': build_py}, +) diff --git a/tests/dtds/HTML4-f.dtd b/tests/dtds/HTML4-f.dtd new file mode 100644 index 0000000..9552012 --- /dev/null +++ b/tests/dtds/HTML4-f.dtd @@ -0,0 +1,37 @@ + + + + + ... + + + ... + + +--> + + + +%HTML4.dtd; \ No newline at end of file diff --git a/tests/dtds/HTML4-s.dtd b/tests/dtds/HTML4-s.dtd new file mode 100644 index 0000000..8ce7917 --- /dev/null +++ b/tests/dtds/HTML4-s.dtd @@ -0,0 +1,869 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/dtds/HTML4.dcl b/tests/dtds/HTML4.dcl new file mode 100644 index 0000000..db46db0 --- /dev/null +++ b/tests/dtds/HTML4.dcl @@ -0,0 +1,88 @@ + \ No newline at end of file diff --git a/tests/dtds/HTML4.dtd b/tests/dtds/HTML4.dtd new file mode 100644 index 0000000..9e781db --- /dev/null +++ b/tests/dtds/HTML4.dtd @@ -0,0 +1,1092 @@ + + + + + ... + + + ... + + + + The URI used as a system identifier with the public identifier allows + the user agent to download the DTD and entity sets as needed. + + The FPI for the Strict HTML 4.0 DTD is: + + "-//W3C//DTD HTML 4.0//EN" + + and its URI is: + + http://www.w3.org/TR/REC-html40/strict.dtd + + Authors should use the Strict DTD unless they need the + presentation control for user agents that don't (adequately) + support style sheets. + + If you are writing a document that includes frames, use + the following FPI: + + "-//W3C//DTD HTML 4.0 Frameset//EN" + + with the URI: + + http://www.w3.org/TR/REC-html40/frameset.dtd + + The following URIs are supported in relation to HTML 4.0 + + "http://www.w3.org/TR/REC-html40/strict.dtd" (Strict DTD) + "http://www.w3.org/TR/REC-html40/loose.dtd" (Loose DTD) + "http://www.w3.org/TR/REC-html40/frameset.dtd" (Frameset DTD) + "http://www.w3.org/TR/REC-html40/HTMLlat1.ent" (Latin-1 entities) + "http://www.w3.org/TR/REC-html40/HTMLsymbol.ent" (Symbol entities) + "http://www.w3.org/TR/REC-html40/HTMLspecial.ent" (Special entities) + + These URIs point to the latest version of each file. To reference + this specific revision use the following URIs: + + "http://www.w3.org/TR/REC-html40-971218/strict.dtd" + "http://www.w3.org/TR/REC-html40-971218/loose.dtd" + "http://www.w3.org/TR/REC-html40-971218/frameset.dtd" + "http://www.w3.org/TR/REC-html40-971218/HTMLlat1.ent" + "http://www.w3.org/TR/REC-html40-971218/HTMLsymbol.ent" + "http://www.w3.org/TR/REC-html40-971218/HTMLspecial.ent" + +--> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + +]]> + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + diff --git a/tests/dtds/HTML4.soc b/tests/dtds/HTML4.soc new file mode 100644 index 0000000..ec4825f --- /dev/null +++ b/tests/dtds/HTML4.soc @@ -0,0 +1,9 @@ +OVERRIDE YES +SGMLDECL HTML4.dcl +DOCTYPE HTML HTML4.dtd +PUBLIC "-//W3C//DTD HTML 4.0//EN" HTML4-s.dtd +PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" HTML4.dtd +PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" HTML4-f.dtd +PUBLIC "-//W3C//ENTITIES Latin1//EN//HTML" HTMLlat1.ent +PUBLIC "-//W3C//ENTITIES Special//EN//HTML" HTMLspec.ent +PUBLIC "-//W3C//ENTITIES Symbols//EN//HTML" HTMLsym.ent diff --git a/tests/dtds/HTMLlat1.ent b/tests/dtds/HTMLlat1.ent new file mode 100644 index 0000000..7632023 --- /dev/null +++ b/tests/dtds/HTMLlat1.ent @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/dtds/HTMLspec.ent b/tests/dtds/HTMLspec.ent new file mode 100644 index 0000000..29011cc --- /dev/null +++ b/tests/dtds/HTMLspec.ent @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/dtds/HTMLsym.ent b/tests/dtds/HTMLsym.ent new file mode 100644 index 0000000..2a6250b --- /dev/null +++ b/tests/dtds/HTMLsym.ent @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/examplefiles/ANTLRv3.g b/tests/examplefiles/ANTLRv3.g new file mode 100644 index 0000000..fbe6d65 --- /dev/null +++ b/tests/examplefiles/ANTLRv3.g @@ -0,0 +1,608 @@ +/* + [The "BSD licence"] + Copyright (c) 2005-2007 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** ANTLR v3 grammar written in ANTLR v3 with AST construction */ +grammar ANTLRv3; + +options { + output=AST; + ASTLabelType=CommonTree; +} + +tokens { + DOC_COMMENT; + PARSER; + LEXER; + RULE; + BLOCK; + OPTIONAL; + CLOSURE; + POSITIVE_CLOSURE; + SYNPRED; + RANGE; + CHAR_RANGE; + EPSILON; + ALT; + EOR; + EOB; + EOA; // end of alt + ID; + ARG; + ARGLIST; + RET; + LEXER_GRAMMAR; + PARSER_GRAMMAR; + TREE_GRAMMAR; + COMBINED_GRAMMAR; + INITACTION; + LABEL; // $x used in rewrite rules + TEMPLATE; + SCOPE='scope'; + SEMPRED; + GATED_SEMPRED; // {p}? => + SYN_SEMPRED; // (...) => it's a manually-specified synpred converted to sempred + BACKTRACK_SEMPRED; // auto backtracking mode syn pred converted to sempred + FRAGMENT='fragment'; + TREE_BEGIN='^('; + ROOT='^'; + BANG='!'; + RANGE='..'; + REWRITE='->'; +} + +@members { + int gtype; +} + +grammarDef + : DOC_COMMENT? + ( 'lexer' {gtype=LEXER_GRAMMAR;} // pure lexer + | 'parser' {gtype=PARSER_GRAMMAR;} // pure parser + | 'tree' {gtype=TREE_GRAMMAR;} // a tree parser + | {gtype=COMBINED_GRAMMAR;} // merged parser/lexer + ) + g='grammar' id ';' optionsSpec? tokensSpec? attrScope* action* + rule+ + EOF + -> ^( {adaptor.create(gtype,$g)} + id DOC_COMMENT? optionsSpec? tokensSpec? attrScope* action* rule+ + ) + ; + +tokensSpec + : TOKENS tokenSpec+ '}' -> ^(TOKENS tokenSpec+) + ; + +tokenSpec + : TOKEN_REF + ( '=' (lit=STRING_LITERAL|lit=CHAR_LITERAL) -> ^('=' TOKEN_REF $lit) + | -> TOKEN_REF + ) + ';' + ; + +attrScope + : 'scope' id ACTION -> ^('scope' id ACTION) + ; + +/** Match stuff like @parser::members {int i;} */ +action + : '@' (actionScopeName '::')? id ACTION -> ^('@' actionScopeName? id ACTION) + ; + +/** Sometimes the scope names will collide with keywords; allow them as + * ids for action scopes. + */ +actionScopeName + : id + | l='lexer' -> ID[$l] + | p='parser' -> ID[$p] + ; + +optionsSpec + : OPTIONS (option ';')+ '}' -> ^(OPTIONS option+) + ; + +option + : id '=' optionValue -> ^('=' id optionValue) + ; + +optionValue + : id + | STRING_LITERAL + | CHAR_LITERAL + | INT + | s='*' -> STRING_LITERAL[$s] // used for k=* + ; + +rule +scope { + String name; +} + : DOC_COMMENT? + ( modifier=('protected'|'public'|'private'|'fragment') )? + id {$rule::name = $id.text;} + '!'? + ( arg=ARG_ACTION )? + ( 'returns' rt=ARG_ACTION )? + throwsSpec? optionsSpec? ruleScopeSpec? ruleAction* + ':' altList ';' + exceptionGroup? + -> ^( RULE id {modifier!=null?adaptor.create(modifier):null} ^(ARG $arg)? ^(RET $rt)? + optionsSpec? ruleScopeSpec? ruleAction* + altList + exceptionGroup? + EOR["EOR"] + ) + ; + +/** Match stuff like @init {int i;} */ +ruleAction + : '@' id ACTION -> ^('@' id ACTION) + ; + +throwsSpec + : 'throws' id ( ',' id )* -> ^('throws' id+) + ; + +ruleScopeSpec + : 'scope' ACTION -> ^('scope' ACTION) + | 'scope' id (',' id)* ';' -> ^('scope' id+) + | 'scope' ACTION + 'scope' id (',' id)* ';' + -> ^('scope' ACTION id+ ) + ; + +block + : lp='(' + ( (opts=optionsSpec)? ':' )? + a1=alternative rewrite ( '|' a2=alternative rewrite )* + rp=')' + -> ^( BLOCK[$lp,"BLOCK"] optionsSpec? alternative+ EOB[$rp,"EOB"] ) + ; + +altList +@init { + // must create root manually as it's used by invoked rules in real antlr tool. + // leave here to demonstrate use of {...} in rewrite rule + // it's really BLOCK[firstToken,"BLOCK"]; set line/col to previous ( or : token. + CommonTree blkRoot = (CommonTree)adaptor.create(BLOCK,input.LT(-1),"BLOCK"); +} + : a1=alternative rewrite ( '|' a2=alternative rewrite )* + -> ^( {blkRoot} (alternative rewrite?)+ EOB["EOB"] ) + ; + +alternative +@init { + Token firstToken = input.LT(1); + Token prevToken = input.LT(-1); // either : or | I think +} + : element+ -> ^(ALT[firstToken,"ALT"] element+ EOA["EOA"]) + | -> ^(ALT[prevToken,"ALT"] EPSILON[prevToken,"EPSILON"] EOA["EOA"]) + ; + +exceptionGroup + : ( exceptionHandler )+ ( finallyClause )? + | finallyClause + ; + +exceptionHandler + : 'catch' ARG_ACTION ACTION -> ^('catch' ARG_ACTION ACTION) + ; + +finallyClause + : 'finally' ACTION -> ^('finally' ACTION) + ; + +element + : elementNoOptionSpec + ; + +elementNoOptionSpec + : id (labelOp='='|labelOp='+=') atom + ( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK["BLOCK"] ^(ALT["ALT"] ^($labelOp id atom) EOA["EOA"]) EOB["EOB"])) + | -> ^($labelOp id atom) + ) + | id (labelOp='='|labelOp='+=') block + ( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK["BLOCK"] ^(ALT["ALT"] ^($labelOp id block) EOA["EOA"]) EOB["EOB"])) + | -> ^($labelOp id block) + ) + | atom + ( ebnfSuffix -> ^(BLOCK["BLOCK"] ^(ALT["ALT"] atom EOA["EOA"]) EOB["EOB"]) + | -> atom + ) + | ebnf + | ACTION + | SEMPRED ( '=>' -> GATED_SEMPRED | -> SEMPRED ) + | treeSpec + ; + +atom: range ( (op='^'|op='!') -> ^($op range) | -> range ) + | terminal + | notSet ( (op='^'|op='!') -> ^($op notSet) | -> notSet ) + | RULE_REF ( arg=ARG_ACTION )? ( (op='^'|op='!') )? + -> {$arg!=null&&op!=null}? ^($op RULE_REF $arg) + -> {$arg!=null}? ^(RULE_REF $arg) + -> {$op!=null}? ^($op RULE_REF) + -> RULE_REF + ; + +notSet + : '~' + ( notTerminal -> ^('~' notTerminal) + | block -> ^('~' block) + ) + ; + +treeSpec + : '^(' element ( element )+ ')' -> ^(TREE_BEGIN element+) + ; + +/** Matches ENBF blocks (and token sets via block rule) */ +ebnf +@init { + Token firstToken = input.LT(1); +} +@after { + $ebnf.tree.getToken().setLine(firstToken.getLine()); + $ebnf.tree.getToken().setCharPositionInLine(firstToken.getCharPositionInLine()); +} + : block {Token op=input.LT(1);} + ( '?' -> ^(OPTIONAL[op] block) + | '*' -> ^(CLOSURE[op] block) + | '+' -> ^(POSITIVE_CLOSURE[op] block) + | '^' -> ^('^' block) + | '!' -> ^('!' block) + | '=>' // syntactic predicate + -> {gtype==COMBINED_GRAMMAR && + Character.isUpperCase($rule::name.charAt(0))}? + // if lexer rule in combined, leave as pred for lexer + ^(SYNPRED["=>"] block) + // in real antlr tool, text for SYN_SEMPRED is predname + -> SYN_SEMPRED + | -> block + ) + ; + +range! + : c1=CHAR_LITERAL RANGE c2=CHAR_LITERAL -> ^(CHAR_RANGE[$c1,".."] $c1 $c2) + ; + +terminal + : ( CHAR_LITERAL -> CHAR_LITERAL + // Args are only valid for lexer rules + | TOKEN_REF + ( ARG_ACTION -> ^(TOKEN_REF ARG_ACTION) + | -> TOKEN_REF + ) + | STRING_LITERAL -> STRING_LITERAL + | '.' -> '.' + ) + ( '^' -> ^('^' $terminal) + | '!' -> ^('!' $terminal) + )? + ; + +notTerminal + : CHAR_LITERAL + | TOKEN_REF + | STRING_LITERAL + ; + +ebnfSuffix +@init { + Token op = input.LT(1); +} + : '?' -> OPTIONAL[op] + | '*' -> CLOSURE[op] + | '+' -> POSITIVE_CLOSURE[op] + ; + + + +// R E W R I T E S Y N T A X + +rewrite +@init { + Token firstToken = input.LT(1); +} + : (rew+='->' preds+=SEMPRED predicated+=rewrite_alternative)* + rew2='->' last=rewrite_alternative + -> ^($rew $preds $predicated)* ^($rew2 $last) + | + ; + +rewrite_alternative + : rewrite_template + | rewrite_tree_alternative + | /* empty rewrite */ -> ^(ALT["ALT"] EPSILON["EPSILON"] EOA["EOA"]) + ; + +rewrite_template_block + : lp='(' rewrite_template ')' -> ^(BLOCK[$lp,"BLOCK"] rewrite_template EOB[$lp,"EOB"]) + ; + +rewrite_tree_block + : lp='(' rewrite_tree_alternative ')' + -> ^(BLOCK[$lp,"BLOCK"] rewrite_tree_alternative EOB[$lp,"EOB"]) + ; + +rewrite_tree_alternative + : rewrite_tree_element+ -> ^(ALT["ALT"] rewrite_tree_element+ EOA["EOA"]) + ; + +rewrite_tree_element + : rewrite_tree_atom + | rewrite_tree_atom ebnfSuffix + -> ^( ebnfSuffix ^(BLOCK["BLOCK"] ^(ALT["ALT"] rewrite_tree_atom EOA["EOA"]) EOB["EOB"])) + | rewrite_tree + ( ebnfSuffix + -> ^(BLOCK["BLOCK"] ^(ALT["ALT"] rewrite_tree EOA["EOA"]) EOB["EOB"]) + | -> rewrite_tree + ) + | rewrite_tree_ebnf + ; + +rewrite_tree_atom + : CHAR_LITERAL + | TOKEN_REF ARG_ACTION? -> ^(TOKEN_REF ARG_ACTION?) // for imaginary nodes + | RULE_REF + | STRING_LITERAL + | d='$' id -> LABEL[$d,$id.text] // reference to a label in a rewrite rule + | ACTION + ; + +rewrite_tree_ebnf +@init { + Token firstToken = input.LT(1); +} +@after { + $rewrite_tree_ebnf.tree.getToken().setLine(firstToken.getLine()); + $rewrite_tree_ebnf.tree.getToken().setCharPositionInLine(firstToken.getCharPositionInLine()); +} + : rewrite_tree_block ebnfSuffix -> ^(ebnfSuffix rewrite_tree_block) + ; + +rewrite_tree + : '^(' rewrite_tree_atom rewrite_tree_element* ')' + -> ^(TREE_BEGIN rewrite_tree_atom rewrite_tree_element* ) + ; + +/** Build a tree for a template rewrite: + ^(TEMPLATE (ID|ACTION) ^(ARGLIST ^(ARG ID ACTION) ...) ) + where ARGLIST is always there even if no args exist. + ID can be "template" keyword. If first child is ACTION then it's + an indirect template ref + + -> foo(a={...}, b={...}) + -> ({string-e})(a={...}, b={...}) // e evaluates to template name + -> {%{$ID.text}} // create literal template from string (done in ActionTranslator) + -> {st-expr} // st-expr evaluates to ST + */ +rewrite_template + : // -> template(a={...},...) "..." inline template + {input.LT(1).getText().equals("template")}? + id lp='(' rewrite_template_args ')' + st=( DOUBLE_QUOTE_STRING_LITERAL | DOUBLE_ANGLE_STRING_LITERAL ) + -> ^(TEMPLATE[$lp,"TEMPLATE"] id rewrite_template_args $st) + + | // -> foo(a={...}, ...) + rewrite_template_ref + + | // -> ({expr})(a={...}, ...) + rewrite_indirect_template_head + + | // -> {...} + ACTION + ; + +/** -> foo(a={...}, ...) */ +rewrite_template_ref + : id lp='(' rewrite_template_args ')' + -> ^(TEMPLATE[$lp,"TEMPLATE"] id rewrite_template_args) + ; + +/** -> ({expr})(a={...}, ...) */ +rewrite_indirect_template_head + : lp='(' ACTION ')' '(' rewrite_template_args ')' + -> ^(TEMPLATE[$lp,"TEMPLATE"] ACTION rewrite_template_args) + ; + +rewrite_template_args + : rewrite_template_arg (',' rewrite_template_arg)* + -> ^(ARGLIST rewrite_template_arg+) + | -> ARGLIST + ; + +rewrite_template_arg + : id '=' ACTION -> ^(ARG[$id.start] id ACTION) + ; + +id : TOKEN_REF -> ID[$TOKEN_REF] + | RULE_REF -> ID[$RULE_REF] + ; + +// L E X I C A L R U L E S + +SL_COMMENT + : '//' + ( ' $ANTLR ' SRC // src directive + | ~('\r'|'\n')* + ) + '\r'? '\n' + {$channel=HIDDEN;} + ; + +ML_COMMENT + : '/*' {if (input.LA(1)=='*') $type=DOC_COMMENT; else $channel=HIDDEN;} .* '*/' + ; + +CHAR_LITERAL + : '\'' LITERAL_CHAR '\'' + ; + +STRING_LITERAL + : '\'' LITERAL_CHAR LITERAL_CHAR* '\'' + ; + +fragment +LITERAL_CHAR + : ESC + | ~('\''|'\\') + ; + +DOUBLE_QUOTE_STRING_LITERAL + : '"' LITERAL_CHAR* '"' + ; + +DOUBLE_ANGLE_STRING_LITERAL + : '<<' .* '>>' + ; + +fragment +ESC : '\\' + ( 'n' + | 'r' + | 't' + | 'b' + | 'f' + | '"' + | '\'' + | '\\' + | '>' + | 'u' XDIGIT XDIGIT XDIGIT XDIGIT + | . // unknown, leave as it is + ) + ; + +fragment +XDIGIT : + '0' .. '9' + | 'a' .. 'f' + | 'A' .. 'F' + ; + +INT : '0'..'9'+ + ; + +ARG_ACTION + : NESTED_ARG_ACTION + ; + +fragment +NESTED_ARG_ACTION : + '[' + ( options {greedy=false; k=1;} + : NESTED_ARG_ACTION + | ACTION_STRING_LITERAL + | ACTION_CHAR_LITERAL + | . + )* + ']' + {setText(getText().substring(1, getText().length()-1));} + ; + +ACTION + : NESTED_ACTION ( '?' {$type = SEMPRED;} )? + ; + +fragment +NESTED_ACTION : + '{' + ( options {greedy=false; k=3;} + : NESTED_ACTION + | SL_COMMENT + | ML_COMMENT + | ACTION_STRING_LITERAL + | ACTION_CHAR_LITERAL + | . + )* + '}' + {$channel = DEFAULT_TOKEN_CHANNEL;} + ; + +fragment +ACTION_CHAR_LITERAL + : '\'' (ACTION_ESC|~('\\'|'\'')) '\'' + ; + +fragment +ACTION_STRING_LITERAL + : '"' (ACTION_ESC|~('\\'|'"'))+ '"' + ; + +fragment +ACTION_ESC + : '\\\'' + | '\\"' + | '\\' ~('\''|'"') + ; + +TOKEN_REF + : 'A'..'Z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')* + ; + +RULE_REF + : 'a'..'z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')* + ; + +/** Match the start of an options section. Don't allow normal + * action processing on the {...} as it's not a action. + */ +OPTIONS + : 'options' WS_LOOP '{' {$channel=DEFAULT_TOKEN_CHANNEL;} // WS_LOOP sets channel + ; + +TOKENS + : 'tokens' WS_LOOP '{' {$channel=DEFAULT_TOKEN_CHANNEL;} + ; + +/** Reset the file and line information; useful when the grammar + * has been generated so that errors are shown relative to the + * original file like the old C preprocessor used to do. + */ +fragment +SRC : 'src' ' ' file=ACTION_STRING_LITERAL ' ' line=INT {$channel=HIDDEN;} + ; + +WS : ( ' ' + | '\t' + | '\r'? '\n' + )+ + {$channel=HIDDEN;} + ; + +fragment +WS_LOOP + : ( WS + | SL_COMMENT + | ML_COMMENT + )* + {$channel=HIDDEN;} + ; + diff --git a/tests/examplefiles/AlternatingGroup.mu b/tests/examplefiles/AlternatingGroup.mu new file mode 100644 index 0000000..2cb1992 --- /dev/null +++ b/tests/examplefiles/AlternatingGroup.mu @@ -0,0 +1,102 @@ +/*++ $Id: AlternatingGroup.mu,v 1.4 2003/09/08 15:00:47 nthiery Exp $ + +Dom::AlternatingGroup(n) -- the Alternating Group of {1..n} + +n - integer >= 1 + +Elements are represented as in Dom::PermutationGroup(n) + +Author: Nicolas M. Thiéry +License: LGPL +Created: August 8th, 1999 +Last update: $Date: 2003/09/08 15:00:47 $ +++*/ + +domain Dom::AlternatingGroup(n: Type::PosInt) + inherits Dom::PermutationGroup(n,toBeDefined); + category Cat::PermutationGroup; + axiom Ax::canonicalRep; + +/*-- + size + + Size of the group. +--*/ + + size := fact(n)/2; + +/*-- + generators + + A list of generators of the group + + The first 3-cycle (1,2,3), and a maximal even cycle (1,...,n) or + (2,...,n) depending on the parity of n + +--*/ + + generators := + if n<=2 then generators:=[dom([[1]])]; + elif n=3 then generators:=[dom([[1,2,3]])]; + elif n mod 2=0 then generators:=[dom([[1,2,3]]), dom([[$2..n]])]; + else generators:=[dom([[1,2,3]]), dom([[$1..n]])]; + end_if; + +/*-- + allElements + + List of all the elements of the group +--*/ + + allElements := + proc() + option remember; + local p; + begin + [new(dom,p) $ p in select(combinat::permutations(n), + p->bool(combinat::permutations::sign(p)=1))]; + end_proc; + +/*-- + cycleTypes: + + Count the elements of the group by cycle type. + (Cf Cat::PermutationGroupModule). + + Same algorithm as for Dom::SymmetricGroup, but only even permutations + are considered. This is done by disregarding partitions p such + that n-length(p) is odd. +--*/ + + cycleTypes := + proc() + option remember; + local t, p, gen; + begin + userinfo(3, "cycleTypes: starting computation"); + t:=table(); + + gen := combinat::partitions::generator(n); + while (p:=gen()) <> FAIL do + userinfo(5, "working on partition", p); + if(n-nops(p) mod 2=0) then + // Compute the size of the conjugacy class of Sn indexed by p + // and the cycle type of a permutation in this conjugacy class + t[combinat::partitions::toExp(p,n)] + := combinat::partitions::conjugacyClassSize(p); + end_if; + end_while; + t; + end_proc; + +begin + if testargs() then + if args(0) <> 1 then error("wrong no of args"); end_if; + if not testtype(n,DOM_INT) then + error("argument must be integer") + end_if; + if n < 1 then + error("argument must be positive") + end_if; + end_if; +end_domain: diff --git a/tests/examplefiles/Constants.mo b/tests/examplefiles/Constants.mo new file mode 100644 index 0000000..a886277 --- /dev/null +++ b/tests/examplefiles/Constants.mo @@ -0,0 +1,158 @@ +within Modelica; +package Constants + "Library of mathematical constants and constants of nature (e.g., pi, eps, R, sigma)" + + import SI = Modelica.SIunits; + import NonSI = Modelica.SIunits.Conversions.NonSIunits; + + extends Modelica.Icons.Library2; + + // Mathematical constants + final constant Real e=Modelica.Math.exp(1.0); + final constant Real pi=2*Modelica.Math.asin(1.0); // 3.14159265358979; + final constant Real D2R=pi/180 "Degree to Radian"; + final constant Real R2D=180/pi "Radian to Degree"; + + // Machine dependent constants + // (the definition is a temporary fix since not adapted to the + // machine where the Modelica translator is running) + final constant Real eps=1.e-15 "Biggest number such that 1.0 + eps = 1.0"; + final constant Real small=1.e-60 + "Smallest number such that small and -small are representable on the machine"; + final constant Real inf=1.e+60 + "Biggest Real number such that inf and -inf are representable on the machine"; + final constant Integer Integer_inf=2147483647 + "Biggest Integer number such that Integer_inf and -Integer_inf are representable on the machine"; + + // Constants of nature + // (name, value, description from http://physics.nist.gov/cuu/Constants/) + final constant SI.Velocity c=299792458 "Speed of light in vacuum"; + final constant SI.Acceleration g_n=9.80665 + "Standard acceleration of gravity on earth"; + final constant Real G(final unit="m3/(kg.s2)") = 6.6742e-11 + "Newtonian constant of gravitation"; + final constant SI.FaradayConstant F = 9.64853399e4 "Faraday constant, C/mol"; + final constant Real h(final unit="J.s") = 6.6260693e-34 "Planck constant"; + final constant Real k(final unit="J/K") = 1.3806505e-23 "Boltzmann constant"; + final constant Real R(final unit="J/(mol.K)") = 8.314472 "Molar gas constant"; + final constant Real sigma(final unit="W/(m2.K4)") = 5.670400e-8 + "Stefan-Boltzmann constant"; + final constant Real N_A(final unit="1/mol") = 6.0221415e23 + "Avogadro constant"; + final constant Real mue_0(final unit="N/A2") = 4*pi*1.e-7 "Magnetic constant"; + final constant Real epsilon_0(final unit="F/m") = 1/(mue_0*c*c) + "Electric constant"; + final constant NonSI.Temperature_degC T_zero=-273.15 + "Absolute zero temperature"; + + annotation ( + Documentation(info=" +

+This package provides often needed constants from mathematics, machine +dependent constants and constants from nature. The latter constants +(name, value, description) are from the following source: +

+ +
+
Peter J. Mohr and Barry N. Taylor (1999):
+
CODATA Recommended Values of the Fundamental Physical Constants: 1998. + Journal of Physical and Chemical Reference Data, Vol. 28, No. 6, 1999 and + Reviews of Modern Physics, Vol. 72, No. 2, 2000. See also http://physics.nist.gov/cuu/Constants/
+
+ +

CODATA is the Committee on Data for Science and Technology.

+ +
+
Main Author:
+
Martin Otter
+ Deutsches Zentrum für Luft und Raumfahrt e. V. (DLR)
+ Oberpfaffenhofen
+ Postfach 11 16
+ D-82230 Weßling
+ email: Martin.Otter@dlr.de
+
+ + +

+Copyright © 1998-2009, Modelica Association and DLR. +

+

+This Modelica package is free software; it can be redistributed and/or modified +under the terms of the Modelica license, see the license conditions +and the accompanying disclaimer +here. +


+ +", revisions=" +
    +
  • Nov 8, 2004 + by Christian Schweiger:
    + Constants updated according to 2002 CODATA values.
  • +
  • Dec 9, 1999 + by Martin Otter:
    + Constants updated according to 1998 CODATA values. Using names, values + and description text from this source. Included magnetic and + electric constant.
  • +
  • Sep 18, 1999 + by Martin Otter:
    + Constants eps, inf, small introduced.
  • +
  • Nov 15, 1997 + by Martin Otter:
    + Realized.
  • +
+"), + Invisible=true, + Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100,-100},{100, + 100}}), graphics={ + Line( + points={{-34,-38},{12,-38}}, + color={0,0,0}, + thickness=0.5), + Line( + points={{-20,-38},{-24,-48},{-28,-56},{-34,-64}}, + color={0,0,0}, + thickness=0.5), + Line( + points={{-2,-38},{2,-46},{8,-56},{14,-64}}, + color={0,0,0}, + thickness=0.5)}), + Diagram(graphics={ + Rectangle( + extent={{200,162},{380,312}}, + fillColor={235,235,235}, + fillPattern=FillPattern.Solid, + lineColor={0,0,255}), + Polygon( + points={{200,312},{220,332},{400,332},{380,312},{200,312}}, + fillColor={235,235,235}, + fillPattern=FillPattern.Solid, + lineColor={0,0,255}), + Polygon( + points={{400,332},{400,182},{380,162},{380,312},{400,332}}, + fillColor={235,235,235}, + fillPattern=FillPattern.Solid, + lineColor={0,0,255}), + Text( + extent={{210,302},{370,272}}, + lineColor={160,160,164}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + textString="Library"), + Line( + points={{266,224},{312,224}}, + color={0,0,0}, + thickness=1), + Line( + points={{280,224},{276,214},{272,206},{266,198}}, + color={0,0,0}, + thickness=1), + Line( + points={{298,224},{302,216},{308,206},{314,198}}, + color={0,0,0}, + thickness=1), + Text( + extent={{152,412},{458,334}}, + lineColor={255,0,0}, + textString="Modelica.Constants")})); +end Constants; diff --git a/tests/examplefiles/DancingSudoku.lhs b/tests/examplefiles/DancingSudoku.lhs new file mode 100644 index 0000000..368ab8e --- /dev/null +++ b/tests/examplefiles/DancingSudoku.lhs @@ -0,0 +1,411 @@ + A Sukodku solver by Chris Kuklewicz (haskell (at) list (dot) mightyreason (dot) com) + The usual BSD license applies, copyright 2006. + Uploaded to HaskellWiki as DancingSudoku.lhs + + I compile on a powerbook G4 (Mac OS X, ghc 6.4.2) using + ghc -optc-O3 -funbox-strict-fields -O2 --make -fglasgow-exts + + This is a translation of Knuth's GDANCE from dance.w / dance.c + + http://www-cs-faculty.stanford.edu/~uno/preprints.html + http://www-cs-faculty.stanford.edu/~uno/programs.html + http://en.wikipedia.org/wiki/Dancing_Links + + I have an older verison that uses lazy ST to return the solutions on + demand, which was more useful when trying to generate new puzzles to + solve. + +> module Main where + +> import Prelude hiding (read) +> import Control.Monad +> import Control.Monad.Fix +> import Data.Array.IArray +> import Control.Monad.ST.Strict +> import Data.STRef.Strict +> import Data.Char(intToDigit,digitToInt) +> import Data.List(unfoldr,intersperse,inits) + +> new = newSTRef +> {-# INLINE new #-} +> read = readSTRef +> {-# INLINE read #-} +> write = writeSTRef +> {-# INLINE write #-} +> modify = modifySTRef +> {-# INLINE modify #-} + + Data types to prevent mixing different index and value types + +> type A = Int +> newtype R = R A deriving (Show,Read,Eq,Ord,Ix,Enum) +> newtype C = C A deriving (Show,Read,Eq,Ord,Ix,Enum) +> newtype V = V A deriving (Show,Read,Eq,Ord,Ix,Enum) +> newtype B = B A deriving (Show,Read,Eq,Ord,Ix,Enum) + + Sudoku also has block constraints, so we want to look up a block + index in an array: + +> lookupBlock :: Array (R,C) B +> lookupBlock = listArray bb [ toBlock ij | ij <- range bb ] +> where ra :: Array Int B +> ra = listArray (0,pred (rangeSize b)) [B (fst b) .. B (snd b)] +> toBlock (R i,C j) = ra ! ( (div (index b j) 3)+3*(div (index b i) 3) ) + + The values for an unknown location is 'u'. + The bound and range are given by b and rng. And bb is a 2D bound. + +> u = V 0 -- unknown value +> b :: (Int,Int) +> b = (1,9) -- min and max bounds +> rng = enumFromTo (fst b) (snd b) -- list from '1' to '9' +> bb = ((R (fst b),C (fst b)),(R (snd b),C (snd b))) + + A Spec can be turned into a parsed array with ease: + +> type Hint = ((R,C),V) +> newtype Spec = Spec [Hint] deriving (Eq,Show) + +> type PA = Array (R,C) V + +> parse :: Spec -> PA +> parse (Spec parsed) = let acc old new = new +> in accumArray acc u bb parsed + + The dancing links algorithm depends on a sparse 2D node structure. + Each column represents a constraint. Each row represents a Hint. + The number of possible hints is 9x9x9 = 271 + +> type (MutInt st) = (STRef st) Int + + The pointer types: + +> type (NodePtr st) = (STRef st) (Node st) +> type (HeadPtr st) = (STRef st) (Head st) + + The structures is a 2D grid of nodes, with Col's on the top of + columns and a sparse collection of nodes. Note that topNode of Head + is not a strict field. This is because the topNode needs to refer to + the Head, and they are both created monadically. + +> type HeadName = (Int,Int,Int) -- see below for meaning + +> data Head st = Head {headName:: !HeadName +> ,topNode:: (Node st) -- header node for this column +> ,len:: !(MutInt st) -- number of nodes below this head +> ,next,prev:: !(HeadPtr st) -- doubly-linked list +> } + +> data Node st = Node {getHint:: !Hint +> ,getHead:: !(Head st) -- head for the column this node is in +> ,up,down,left,right :: !(NodePtr st) -- two doubly-linked lists +> } + +> instance Eq (Head st) where +> a == b = headName a == headName b + +> instance Eq (Node st) where +> a == b = up a == up b + + To initialize the structures is a bit tedious. Knuth's code reads in + the problem description from a data file and builds the structure + based on that. Rather than short strings, I will use HeadName as the + identifier. + + The columns are (0,4,5) for nodes that put some value in Row 4 Col 5 + (1,2,3) for nodes that put Val 3 in Row 2 and some column + (2,7,4) for nodes that put Val 4 in Col 7 and some row + (3,1,8) for nodes that put Val 8 in some (row,column) in Block 1 + + The first head is (0,0,0) which is the root. The non-root head data + will be put in an array with the HeadName as an index. + +> headNames :: [HeadName] +> headNames = let names = [0,1,2,3] +> in (0,0,0):[ (l,i,j) | l<-names,i<-rng,j<-rng] + + A "row" of left-right linked nodes is a move. It is defined by a + list of head names. + +> type Move = [(Hint,HeadName)] + + Initial hints are enforced by making them the only legal move for + that location. Blank entries with value 'u = V 0' have a move for + all possible values [V 1..V 9]. + +> parseSpec :: Spec -> [Move] +> parseSpec spec = +> let rowsFrom :: Hint -> [Move] +> rowsFrom (rc@(R r,C c),mv@(V v')) = +> if mv == u then [ rsyms v | v <- rng ] +> else [ rsyms v' ] +> where (B b) = lookupBlock ! rc +> rsyms :: A -> Move +> rsyms v = map ( (,) (rc,V v) ) [(0,r,c),(1,r,v),(2,c,v),(3,b,v)] +> in concatMap rowsFrom (assocs (parse spec)) + + mkDList creates doubly linked lists using a monadic smart + constructor and the recursive "mdo" notation as documented at + http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#mdo-notation + http://www.cse.ogi.edu/PacSoft/projects/rmb/ + + For more fun with this, see the wiki page at + http://haskell.org/hawiki/TyingTheKnot + +> mkDList :: (MonadFix m) => (b -> a -> b -> m b) -> [a] -> m b +> mkDList _ [] = error "must have at least one element" +> mkDList mkNode xs = mdo (first,last) <- go last xs first +> return first +> where go prev [] next = return (next,prev) +> go prev (x:xs) next = mdo this <- mkNode prev x rest +> (rest,last) <- go this xs next +> return (this,last) + + toSimple takes a function and a header node and iterates (read . function) + until the header is reached again, but does not return the header + itself. + +> toSingle step header = loop =<< (read . step) header +> where loop y = if header/=y then liftM (y:) (read (step y) >>= loop) +> else return [] +> + + forEach is an optimization of (toSimple step header >>= mapM_ act) + +> forEach step header act = loop =<< (read . step) header +> where loop y = if header/=y then (act y >> (read (step y)) >>= loop) +> else return () + + Now make the root node and all the head nodes. This also exploits mdo: + +> makeHeads :: [HeadName] -> (ST st) (Head st) +> makeHeads names = mkDList makeHead names +> where makeHead before name after = mdo +> ~newTopNode <- liftM4 (Node ((R 0,C 0),V 0) newHead) (new newTopNode) (new newTopNode) +> (new newTopNode) (new newTopNode) +> newHead <- liftM3 (Head name newTopNode) +> (new 0) (new after) (new before) +> return newHead + + The Head nodes will be places in an array for easy lookup while building moves: + +> type HArray st = Array HeadName (Head st) +> hBounds = ((0,1,1),(3,9,9)) +> type Root st = (Head st,HArray st) + + The addMove function creates the (four) nodes that represent a move and adds + them to the data structure. The HArray in Root makes for a fast + lookup of the Head data. + +> addMove :: forall st. (Root st) -> Move -> (ST st) (Node st) +> addMove (_,ha) move = mkDList addNode move +> where addNode :: (Node st) -> (Hint,HeadName) -> (Node st) -> (ST st) (Node st) +> addNode before (hint,name) after = do +> let head = ha ! name +> let below = topNode head +> above <- read (up below) +> newNode <- liftM4 (Node hint head) (new above) (new below) +> (new before) (new after) +> write (down above) newNode +> write (up below) newNode +> modify (len head) succ +> l <- read (len head) +> seq l (return newNode) + + Create the column headers, including the fast lookup array. These + will be resused between puzzles. + +> initHA :: (ST st) (Root st) +> initHA = do +> root <- makeHeads headNames +> heads <- toSingle next root +> let ha = array hBounds (zip (map headName heads) heads) +> return (root,ha) + + Take the Root from initHA and a puzzle Spec and fill in all the Nodes. + +> initRoot :: (Root st) -> Spec -> (ST st) () +> initRoot root spec = do +> let moves = parseSpec spec +> mapM_ (addMove root) moves + + Return the column headers to their condition after initHA + +> resetRoot :: (Root st) -> (ST st) () +> resetRoot (root,ha) = do +> let heads@(first:_) = elems ha +> let resetHead head = do +> write (len head) 0 +> let node = topNode head +> write (down node) node +> write (up node) node +> reset (last:[]) = do +> write (prev root) last +> write (next root) first +> reset (before:xs@(head:[])) = do +> resetHead head +> write (prev head) before +> write (next head) root +> reset xs +> reset (before:xs@(head:after:_)) = do +> resetHead head +> write (prev head) before +> write (next head) after +> reset xs +> reset (root:heads) + + getBest iterates over the unmet constraints (i.e. the Head that are + reachable from root). It locates the one with the lowest number of + possible moves that will solve it, aborting early if it finds 0 or 1 + moves. + +> getBest :: (Head st) -> (ST st) (Maybe (Head st)) +> getBest root = do +> first <- read (next root) +> if first == root then return Nothing +> else do +> let findMin m best head | head == root = return (Just best) +> | otherwise = do +> l <- read (len head) +> if l <= 1 then return (Just head) +> else if l < m then findMin l head =<< read (next head) +> else findMin l best =<< read (next head) +> findMin 10 first first + + The unlink and relink operations are from where Knuth got the name + "dancing links". So long as "a" does not change in between, the + relink call will undo the unlink call. Similarly, the unconver will + undo the changes of cover and unconverOthers will undo coverOthers. + +> unlink :: (a->STRef st a) -> (a->STRef st a) -> a -> (ST st) () +> unlink prev next a = do +> before <- read (prev a) +> after <- read (next a) +> write (next before) after +> write (prev after) before + +> relink :: (a->STRef st a) -> (a->STRef st a) -> a -> (ST st) () +> relink prev next a = do +> before <- read (prev a) +> after <- read (next a) +> write (next before) a +> write (prev after) a + +> cover :: (Head st) -> (ST st) () +> cover head = do +> unlink prev next head +> let eachDown rr = forEach right rr eachRight +> eachRight nn = do +> unlink up down nn +> modify (len $ getHead nn) pred +> forEach down (topNode head) eachDown + +> uncover :: (Head st) -> (ST st) () +> uncover head = do +> let eachUp rr = forEach left rr eachLeft +> eachLeft nn = do +> modify (len $ getHead nn) succ +> relink up down nn +> forEach up (topNode head) eachUp +> relink prev next head + +> coverOthers :: (Node st) -> (ST st) () +> coverOthers node = forEach right node (cover . getHead) + +> uncoverOthers :: (Node st) -> (ST st) () +> uncoverOthers node = forEach left node (uncover . getHead) + + A helper function for gdance: + +> choicesToSpec :: [(Node st)] -> Spec +> choicesToSpec = Spec . (map getHint) + + This is the heart of the algorithm. I have altered it to return only + the first solution, or produce an error if none is found. + + Knuth used several goto links to do what is done below with tail + recursion. + +> gdance :: (Head st) -> (ST st) Spec -- [Spec] +> gdance root = +> let +> forward choices = do +> maybeHead <- getBest root +> case maybeHead of +> Nothing -> if null choices +> then error "No choices in forward" -- return [] -- for [Spec] +> else do -- nextSols <- recover choices -- for [Spec] +> return $ (choicesToSpec choices) -- :nextSols -- for [Spec] +> Just head -> do cover head +> startRow <- readSTRef (down (topNode head)) +> advance (startRow:choices) +> +> advance choices@(newRow:oldChoices) = do +> let endOfRows = topNode (getHead newRow) +> if (newRow == endOfRows) +> then do uncover (getHead newRow) +> if (null oldChoices) +> then error "No choices in advance" -- return [] -- for [Spec] +> else recover oldChoices +> else do coverOthers newRow +> forward choices +> +> recover (oldRow:oldChoices) = do +> uncoverOthers oldRow +> newRow <- readSTRef (down oldRow) +> advance (newRow:oldChoices) +> +> in forward [] + + + Convert a text board into a Spec + +> parseBoard :: String -> Spec +> parseBoard s = Spec (zip rcs vs'check) +> where rcs :: [(R,C)] +> rcs = [ (R r,C c) | r <- rng, c <- rng ] +> isUnset c = (c=='.') || (c==' ') || (c=='0') +> isHint c = ('1'<=c) && (c<='9') +> cs = take 81 $ filter (\c -> isUnset c || isHint c) s +> vs :: [V] +> vs = map (\c -> if isUnset c then u else (V $ digitToInt c)) cs +> vs'check = if 81==length vs then vs else error ("parse of board failed\n"++s) + + This is quite useful as a utility function which partitions the list into groups of n elements. + Used by showSpec. + +> groupTake :: Int->[a]->[[a]] +> groupTake n b = unfoldr foo b +> where foo [] = Nothing +> foo b = Just (splitAt n b) + + Make a nice 2D ascii board from the Spec (not used at the moment) + +> showSpec :: Spec -> String +> showSpec spec = let pa = parse spec +> g = groupTake 9 (map (\(V v) -> if v == 0 then '.' else intToDigit v) $ elems pa) +> addV line = concat $ intersperse "|" (groupTake 3 line) +> addH list = concat $ intersperse ["---+---+---"] (groupTake 3 list) +> in unlines $ addH (map addV g) + + One line display + +> showCompact spec = map (\(V v) -> intToDigit v) (elems (parse spec)) + + The main routine is designed to handle the input from http://www.csse.uwa.edu.au/~gordon/sudoku17 + +> main = do +> all <- getContents +> let puzzles = zip [1..] (map parseBoard (lines all)) +> root <- stToIO initHA +> let act :: (Int,Spec) -> IO () +> act (i,spec) = do +> answer <- stToIO (do initRoot root spec +> answer <- gdance (fst root) +> resetRoot root +> return answer) +> print (i,showCompact answer) +> mapM_ act puzzles + +> inits' xn@(_:_) = zipWith take [0..] $ map (const xn) $ undefined:xn +> inits' _ = undefined diff --git a/tests/examplefiles/Errors.scala b/tests/examplefiles/Errors.scala new file mode 100644 index 0000000..67198c0 --- /dev/null +++ b/tests/examplefiles/Errors.scala @@ -0,0 +1,18 @@ +/* This file /* which is totally legal scala */ will not be highlighted + correcty by pygments */ + +object ⌘ { + val `interface` = """ +A +"Multiline" +String +""" + + val foo_+ = "foo plus" + val foo_⌬⌬ = "double benzene" + + def main(argv: Array[String]) { + println(⌘.interface + " " + foo_+ + " " + foo_⌬⌬ ) + } +} + diff --git a/tests/examplefiles/Intro.java b/tests/examplefiles/Intro.java new file mode 100644 index 0000000..66d2fee --- /dev/null +++ b/tests/examplefiles/Intro.java @@ -0,0 +1,1660 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * -Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING + * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT + * BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT + * OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS + * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST + * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, + * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY + * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN + * IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that Software is not designed, licensed or intended for + * use in the design, construction, operation or maintenance of any nuclear + * facility. + */ + + +package java2d; + +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.*; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.font.*; +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.table.*; +import javax.swing.event.*; +import java.util.Vector; +import java.util.List; +import java.util.Arrays; + + + +/** + * Introduction to the Java2Demo. + * + * @version @(#)Intro.java 1.19 03/06/26 + * @author Brian Lichtenwalter + */ +public class Intro extends JPanel { + + static Color black = new Color(20, 20, 20); + static Color white = new Color(240, 240, 255); + static Color red = new Color(149, 43, 42); + static Color blue = new Color(94, 105, 176); + static Color yellow = new Color(255, 255, 140); + + static Surface surface; + private ScenesTable scenesTable; + private boolean doTable; + + + public Intro() { + EmptyBorder eb = new EmptyBorder(80,110,80,110); + BevelBorder bb = new BevelBorder(BevelBorder.LOWERED); + setBorder(new CompoundBorder(eb,bb)); + setLayout(new BorderLayout()); + setBackground(Color.gray); + setToolTipText("click for scene table"); + add(surface = new Surface()); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + removeAll(); + if ((doTable = !doTable)) { + setToolTipText("click for animation"); + surface.stop(); + if (scenesTable == null) { + scenesTable = new ScenesTable(); + } + add(scenesTable); + } else { + setToolTipText("click for scene table"); + surface.start(); + add(surface); + } + revalidate(); + repaint(); + } + }); + } + + + public void start() { + if (!doTable) { + surface.start(); + } + } + + + public void stop() { + if (!doTable) { + surface.stop(); + } + } + + + public static void main(String argv[]) { + final Intro intro = new Intro(); + WindowListener l = new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + public void windowDeiconified(WindowEvent e) { intro.start(); } + public void windowIconified(WindowEvent e) { intro.stop(); } + }; + JFrame f = new JFrame("Java2D Demo - Intro"); + f.addWindowListener(l); + f.getContentPane().add("Center", intro); + f.pack(); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + int w = 720; + int h = 510; + f.setLocation(screenSize.width/2 - w/2, screenSize.height/2 - h/2); + f.setSize(w, h); + f.setVisible(true); + intro.start(); + } + + + /** + * ScenesTable is the list of scenes known to the Director. + * Scene participation, scene name and scene pause amount columns. + * Global animation delay for scene's steps. + */ + static class ScenesTable extends JPanel implements ActionListener, ChangeListener { + + private JTable table; + private TableModel dataModel; + + public ScenesTable() { + setBackground(Color.white); + setLayout(new BorderLayout()); + final String[] names = { "", "Scenes", "Pause" }; + + dataModel = new AbstractTableModel() { + public int getColumnCount() { return names.length; } + public int getRowCount() { return surface.director.size();} + public Object getValueAt(int row, int col) { + Surface.Scene scene = (Surface.Scene) surface.director.get(row); + if (col == 0) { + return scene.participate; + } else if (col == 1) { + return scene.name; + } else { + return scene.pauseAmt; + } + } + public String getColumnName(int col) {return names[col]; } + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + public boolean isCellEditable(int row, int col) { + return col != 1 ? true : false; + } + public void setValueAt(Object aValue, int row, int col) { + Surface.Scene scene = (Surface.Scene) surface.director.get(row); + if (col == 0) { + scene.participate = aValue; + } else if (col == 1) { + scene.name = aValue; + } else { + scene.pauseAmt = aValue; + } + } + }; + + table = new JTable(dataModel); + TableColumn col = table.getColumn(""); + col.setWidth(16); + col.setMinWidth(16); + col.setMaxWidth(20); + col = table.getColumn("Pause"); + col.setWidth(60); + col.setMinWidth(60); + col.setMaxWidth(60); + table.sizeColumnsToFit(0); + + JScrollPane scrollpane = new JScrollPane(table); + add(scrollpane); + + JPanel panel = new JPanel(new BorderLayout()); + JButton b = new JButton("Unselect All"); + b.setHorizontalAlignment(JButton.LEFT); + Font font = new Font("serif", Font.PLAIN, 10); + b.setFont(font); + b.addActionListener(this); + panel.add("West", b); + + JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 200, (int) surface.sleepAmt); + slider.addChangeListener(this); + TitledBorder tb = new TitledBorder(new EtchedBorder()); + tb.setTitleFont(font); + tb.setTitle("Anim delay = " + String.valueOf(surface.sleepAmt) + " ms"); + slider.setBorder(tb); + slider.setPreferredSize(new Dimension(140,40)); + slider.setMinimumSize(new Dimension(100,40)); + slider.setMaximumSize(new Dimension(180,40)); + panel.add("East", slider); + + add("South", panel); + } + + + public void actionPerformed(ActionEvent e) { + JButton b = (JButton) e.getSource(); + b.setSelected(!b.isSelected()); + b.setText(b.isSelected() ? "Select All" : "Unselect All"); + for (int i = 0; i < surface.director.size(); i++) { + Surface.Scene scene = (Surface.Scene) surface.director.get(i); + scene.participate = new Boolean(!b.isSelected()); + } + table.tableChanged(new TableModelEvent(dataModel)); + } + + + public void stateChanged(ChangeEvent e) { + JSlider slider = (JSlider) e.getSource(); + int value = slider.getValue(); + TitledBorder tb = (TitledBorder) slider.getBorder(); + tb.setTitle("Anim delay = " + String.valueOf(value) + " ms"); + surface.sleepAmt = (long) value; + slider.repaint(); + } + } // End ScenesTable class + + + + /** + * Surface is the stage where the Director plays its scenes. + */ + static class Surface extends JPanel implements Runnable { + + static Surface surf; + static Image cupanim, java_logo; + static BufferedImage bimg; + public Director director; + public int index; + public long sleepAmt = 30; + private Thread thread; + + + public Surface() { + surf = this; + setBackground(black); + setLayout(new BorderLayout()); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (thread == null) start(); else stop(); + } + }); + cupanim = DemoImages.getImage("cupanim.gif", this); + java_logo = DemoImages.getImage("java_logo.png", this); + director = new Director(); + } + + + static FontMetrics getMetrics(Font font) { + return surf.getFontMetrics(font); + } + + + public void paint(Graphics g) { + Dimension d = getSize(); + if (d.width <= 0 || d.height <= 0) { + return; + } + if (bimg == null || bimg.getWidth() != d.width || bimg.getHeight() != d.height) { + bimg = getGraphicsConfiguration().createCompatibleImage(d.width, d.height); + // reset future scenes + for (int i = index+1; i < director.size(); i++) { + ((Scene) director.get(i)).reset(d.width, d.height); + } + } + + Scene scene = (Scene) director.get(index); + if (scene.index <= scene.length) { + if (thread != null) { + scene.step(d.width, d.height); + } + + Graphics2D g2 = bimg.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2.setBackground(getBackground()); + g2.clearRect(0, 0, d.width, d.height); + + scene.render(d.width, d.height, g2); + + if (thread != null) { + // increment scene.index after scene.render + scene.index++; + } + g2.dispose(); + } + g.drawImage(bimg, 0, 0, this); + } + + + + public void start() { + if (thread == null) { + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.setName("Intro"); + thread.start(); + } + } + + + public synchronized void stop() { + if (thread != null) { + thread.interrupt(); + } + thread = null; + notifyAll(); + } + + + public void reset() { + index = 0; + Dimension d = getSize(); + for (int i = 0; i < director.size(); i++) { + ((Scene) director.get(i)).reset(d.width, d.height); + } + } + + + public void run() { + + Thread me = Thread.currentThread(); + + while (thread == me && !isShowing() || getSize().width <= 0) { + try { + thread.sleep(500); + } catch (InterruptedException e) { return; } + } + + if (index == 0) { + reset(); + } + + while (thread == me) { + Scene scene = (Scene) director.get(index); + if (((Boolean) scene.participate).booleanValue()) { + repaint(); + try { + thread.sleep(sleepAmt); + } catch (InterruptedException e) { break; } + if (scene.index > scene.length) { + scene.pause(thread); + if (++index >= director.size()) { + reset(); + } + } + } else { + if (++index >= director.size()) { + reset(); + } + } + } + thread = null; + } + + + + /** + * Part is a piece of the scene. Classes must implement Part + * inorder to participate in a scene. + */ + interface Part { + public void reset(int newwidth, int newheight); + public void step(int w, int h); + public void render(int w, int h, Graphics2D g2); + public int getBegin(); + public int getEnd(); + } + + + + /** + * Director is the holder of the scenes, their names & pause amounts + * between scenes. + */ + static class Director extends Vector { + + GradientPaint gp = new GradientPaint(0,40,blue,38,2,black); + Font f1 = new Font("serif", Font.PLAIN, 200); + Font f2 = new Font("serif", Font.PLAIN, 120); + Font f3 = new Font("serif", Font.PLAIN, 72); + Object parts[][][] = { + { { "J - scale text on gradient", "0" }, + { new GpE(GpE.BURI, black, blue, 0, 20), + new TxE("J", f1, TxE.SCI, yellow, 2, 20) } }, + { { "2 - scale & rotate text on gradient" , "0" }, + { new GpE(GpE.BURI, blue, black, 0, 22), + new TxE("2", f1, TxE.RI | TxE.SCI, yellow, 2, 22) } }, + { { "D - scale text on gradient", "0" }, + { new GpE(GpE.BURI, black, blue, 0, 20), + new TxE("D", f1, TxE.SCI, yellow, 2, 20) } }, + { { "Java2D - scale & rotate text on gradient", "1000" }, + { new GpE(GpE.SIH, blue, black, 0, 40), + new TxE("Java2D", f2, TxE.RI | TxE.SCI, yellow, 0, 40) }}, + { { "Previous scene dither dissolve out", "0"}, + { new DdE(0, 20, 1) }}, + { { "Graphics Features", "999" }, + { new Temp(Temp.RECT, null, 0, 15), + new Temp(Temp.IMG, java_logo, 2, 15), + new Temp(Temp.RNA | Temp.INA, java_logo, 16, 130), + new Features(Features.GRAPHICS, 16, 130) }}, + { { "Java2D - texture text on gradient", "1000"}, + { new GpE(GpE.WI, blue, black, 0, 20), + new GpE(GpE.WD, blue, black, 21, 40), + new TpE(TpE.OI | TpE.NF, black, yellow, 4, 0, 10), + new TpE(TpE.OD | TpE.NF, black, yellow, 4, 11, 20), + new TpE(TpE.OI | TpE.NF | TpE.HAF, black, yellow,5,21,40), + new TxE("Java2D", f2, 0, null, 0, 40) }}, + { { "Previous scene random close out", "0"}, + { new CoE(CoE.RAND, 0, 20) } }, + { { "Text Features", "999" }, + { new Temp(Temp.RECT, null, 0, 15), + new Temp(Temp.IMG, java_logo, 2, 15), + new Temp(Temp.RNA | Temp.INA, java_logo, 16, 130), + new Features(Features.TEXT, 16, 130) }}, + { { "Java2D - composite text on texture", "1000"}, + { new TpE(TpE.RI, black, gp, 40, 0, 20), + new TpE(TpE.RD, black, gp, 40, 21, 40), + new TpE(TpE.RI, black, gp, 40, 41, 60), + new TxE("Java2D", f2, TxE.AC, yellow, 0, 60) }}, + { { "Previous scene dither dissolve out", "0"}, + { new DdE(0, 20, 4) }}, + { { "Imaging Features", "999" }, + { new Temp(Temp.RECT, null, 0, 15), + new Temp(Temp.IMG, java_logo, 2, 15), + new Temp(Temp.RNA | Temp.INA, java_logo, 16, 130), + new Features(Features.IMAGES, 16, 130) }}, + { { "Java2D - text on gradient", "1000" }, + { new GpE(GpE.SDH, blue, black, 0, 20), + new GpE(GpE.SIH, blue, black, 21, 40), + new GpE(GpE.SDH, blue, black, 41, 50), + new GpE(GpE.INC | GpE.NF, red, yellow, 0, 50), + new TxE("Java2D", f2, TxE.NOP, null, 0, 50) }}, + { { "Previous scene ellipse close out", "0"}, + { new CoE(CoE.OVAL, 0, 20) } }, + { { "Color Features", "999" }, + { new Temp(Temp.RECT, null, 0, 15), + new Temp(Temp.IMG, java_logo, 2, 15), + new Temp(Temp.RNA | Temp.INA, java_logo, 16, 99), + new Features(Features.COLOR, 16, 99) }}, + { { "Java2D - composite and rotate text on paints", "2000" }, + { new GpE(GpE.BURI, black, blue, 0, 20), + new GpE(GpE.BURD, black, blue, 21, 30), + new TpE(TpE.OI | TpE.HAF, black, blue, 10, 31, 40), + new TxE("Java2D", f2, TxE.AC | TxE.RI, yellow, 0, 40) }}, + { { "Previous scene subimage transform out", "0" }, + { new SiE(60, 60, 0, 40) }}, + { { "CREDITS - transform in", "1000" }, + { new LnE(LnE.ACI | LnE.ZOOMI | LnE.RI, 0, 60), + new TxE("CREDITS", f3, TxE.AC | TxE.SCI, Color.red,20,30), + new TxE("CREDITS", f3, TxE.SCXD, Color.red, 31, 38), + new TxE("CREDITS", f3, TxE.SCXI, Color.red, 39, 48), + new TxE("CREDITS", f3, TxE.SCXD, Color.red, 49, 54), + new TxE("CREDITS", f3, TxE.SCXI, Color.red, 55, 60) }}, + { { "CREDITS - transform out", "0" }, + { new LnE(LnE.ACD | LnE.ZOOMD | LnE.RD, 0, 45), + new TxE("CREDITS", f3, 0, Color.red, 0, 9), + new TxE("CREDITS", f3, TxE.SCD | TxE.RD, Color.red,10,30)}}, + { { "Contributors", "1000" }, + { new Temp(Temp.RECT, null, 0, 30), + new Temp(Temp.IMG, cupanim, 4, 30), + new Temp(Temp.RNA | Temp.INA, cupanim, 31, 200), + new Contributors(34, 200) } }, + }; + + + public Director() { + for (int i = 0; i < parts.length; i++) { + Vector v = new Vector(); + for (int j = 0; j < parts[i][1].length; j++) { + v.addElement(parts[i][1][j]); + } + addElement(new Scene(v, parts[i][0][0], parts[i][0][1])); + } + } + } + + + + /** + * Scene is the manager of the parts. + */ + static class Scene extends Object { + public Object name; + public Object participate = new Boolean(true); + public Object pauseAmt; + public Vector parts; + public int index; + public int length; + + public Scene(Vector parts, Object name, Object pauseAmt) { + this.name = name; + this.parts = parts; + this.pauseAmt = pauseAmt; + for (int i = 0; i < parts.size(); i++) { + if (((Part) parts.get(i)).getEnd() > length) { + length = ((Part) parts.get(i)).getEnd(); + } + } + } + + public void reset(int w, int h) { + index = 0; + for (int i = 0; i < parts.size(); i++) { + ((Part) parts.get(i)).reset(w, h); + } + } + + public void step(int w, int h) { + for (int i = 0; i < parts.size(); i++) { + Part part = (Part) parts.get(i); + if (index >= part.getBegin() && index <= part.getEnd()) { + part.step(w, h); + } + } + } + + public void render(int w, int h, Graphics2D g2) { + for (int i = 0; i < parts.size(); i++) { + Part part = (Part) parts.get(i); + if (index >= part.getBegin() && index <= part.getEnd()) { + part.render(w, h, g2); + } + } + } + + public void pause(Thread thread) { + try { + thread.sleep(Long.parseLong((String) pauseAmt)); + } catch (Exception e) { } + System.gc(); + } + } // End Scene class + + + + /** + * Text Effect. Transformation of characters. Clip or fill. + */ + static class TxE implements Part { + + static final int INC = 1; + static final int DEC = 2; + static final int R = 4; // rotate + static final int RI = R | INC; + static final int RD = R | DEC; + static final int SC = 8; // scale + static final int SCI = SC | INC; + static final int SCD = SC | DEC; + static final int SCX = 16; // scale invert x + static final int SCXI = SCX | SC | INC; + static final int SCXD = SCX | SC | DEC; + static final int SCY = 32; // scale invert y + static final int SCYI = SCY | SC | INC; + static final int SCYD = SCY | SC | DEC; + static final int AC = 64; // AlphaComposite + static final int CLIP = 128; // Clipping + static final int NOP = 512; // No Paint + private int beginning, ending; + private int type; + private double rIncr, sIncr; + private double sx, sy, rotate; + private Shape shapes[], txShapes[]; + private int sw; + private int numRev; + private Paint paint; + + + public TxE(String text, + Font font, + int type, + Paint paint, + int beg, + int end) { + this.type = type; + this.paint = paint; + this.beginning = beg; + this.ending = end; + + setIncrements(2); + + char[] chars = text.toCharArray(); + shapes = new Shape[chars.length]; + txShapes = new Shape[chars.length]; + FontRenderContext frc = new FontRenderContext(null,true,true); + TextLayout tl = new TextLayout(text, font, frc); + sw = (int) tl.getOutline(null).getBounds().getWidth(); + for (int j = 0; j < chars.length; j++) { + String s = String.valueOf(chars[j]); + shapes[j] = new TextLayout(s, font, frc).getOutline(null); + } + } + + + public void setIncrements(double numRevolutions) { + this.numRev = (int) numRevolutions; + rIncr = 360.0 / ((ending - beginning) / numRevolutions); + sIncr = 1.0 / (ending - beginning); + if ((type & SCX) != 0 || (type & SCY) != 0) { + sIncr *= 2; + } + if ((type & DEC) != 0) { + rIncr = -rIncr; + sIncr = -sIncr; + } + } + + + public void reset(int w, int h) { + if (type == SCXI) { + sx = -1.0; sy = 1.0; + } else if (type == SCYI) { + sx = 1.0; sy = -1.0; + } else { + sx = sy = (type & DEC) != 0 ? 1.0 : 0.0; + } + rotate = 0; + } + + + public void step(int w, int h) { + + float charWidth = w/2-sw/2; + + for (int i = 0; i < shapes.length; i++) { + AffineTransform at = new AffineTransform(); + Rectangle2D maxBounds = shapes[i].getBounds(); + at.translate(charWidth, h/2+maxBounds.getHeight()/2); + charWidth += (float) maxBounds.getWidth() + 1; + Shape shape = at.createTransformedShape(shapes[i]); + Rectangle2D b1 = shape.getBounds2D(); + + if ((type & R) != 0) { + at.rotate(Math.toRadians(rotate)); + } + if ((type & SC) != 0) { + at.scale(sx, sy); + } + shape = at.createTransformedShape(shapes[i]); + Rectangle2D b2 = shape.getBounds2D(); + + double xx = (b1.getX()+b1.getWidth()/2) + - (b2.getX()+b2.getWidth()/2); + double yy = (b1.getY()+b1.getHeight()/2) + - (b2.getY()+b2.getHeight()/2); + AffineTransform toCenterAT = new AffineTransform(); + toCenterAT.translate(xx, yy); + toCenterAT.concatenate(at); + txShapes[i] = toCenterAT.createTransformedShape(shapes[i]); + } + // avoid over rotation + if (Math.abs(rotate) <= numRev * 360) { + rotate += rIncr; + if ((type & SCX) != 0) { + sx += sIncr; + } else if ((type & SCY) != 0) { + sy += sIncr; + } else { + sx += sIncr; sy += sIncr; + } + } + } + + + public void render(int w, int h, Graphics2D g2) { + Composite saveAC = null; + if ((type & AC) != 0 && sx > 0 && sx < 1) { + saveAC = g2.getComposite(); + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) sx)); + } + GeneralPath path = null; + if ((type & CLIP) != 0) { + path = new GeneralPath(); + } + if (paint != null) { + g2.setPaint(paint); + } + for (int i = 0; i < txShapes.length; i++) { + if ((type & CLIP) != 0) { + path.append(txShapes[i], false); + } else { + g2.fill(txShapes[i]); + } + } + if ((type & CLIP) != 0) { + g2.clip(path); + } + if (saveAC != null) { + g2.setComposite(saveAC); + } + } + + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End TxE class + + + + + /** + * GradientPaint Effect. Burst, split, horizontal and + * vertical gradient fill effects. + */ + static class GpE implements Part { + + static final int INC = 1; // increasing + static final int DEC = 2; // decreasing + static final int CNT = 4; // center + static final int WID = 8; // width + static final int WI = WID | INC; + static final int WD = WID | DEC; + static final int HEI = 16; // height + static final int HI = HEI | INC; + static final int HD = HEI | DEC; + static final int SPL = 32 | CNT; // split + static final int SIW = SPL | INC | WID; + static final int SDW = SPL | DEC | WID; + static final int SIH = SPL | INC | HEI; + static final int SDH = SPL | DEC | HEI; + static final int BUR = 64 | CNT; // burst + static final int BURI = BUR | INC; + static final int BURD = BUR | DEC; + static final int NF = 128; // no fill + private Color c1, c2; + private int beginning, ending; + private float incr, index; + private Vector rect = new Vector(); + private Vector grad = new Vector(); + private int type; + + + public GpE(int type, Color c1, Color c2, int beg, int end) { + this.type = type; + this.c1 = c1; + this.c2 = c2; + this.beginning = beg; + this.ending = end; + } + + + public void reset(int w, int h) { + incr = 1.0f / (ending - beginning); + if ((type & CNT) != 0) { + incr /= 2.3f; + } + if ((type & CNT) != 0 && (type & INC) != 0) { + index = 0.5f; + } else if ((type & DEC) != 0) { + index = 1.0f; + incr = -incr; + } else { + index = 0.0f; + } + index += incr; + } + + + public void step(int w, int h) { + rect.clear(); + grad.clear(); + + if ((type & WID) != 0) { + float w2 = 0, x1 = 0, x2 = 0; + if ((type & SPL) != 0) { + w2 = w * 0.5f; + x1 = w * (1.0f - index); + x2 = w * index; + } else { + w2 = w * index; + x1 = x2 = w2; + } + rect.addElement(new Rectangle2D.Float(0, 0, w2, h)); + rect.addElement(new Rectangle2D.Float(w2, 0, w-w2, h)); + grad.addElement(new GradientPaint(0,0,c1,x1,0,c2)); + grad.addElement(new GradientPaint(x2,0,c2,w,0,c1)); + } else if ((type & HEI) != 0) { + float h2 = 0, y1 = 0, y2 = 0; + if ((type & SPL) != 0) { + h2 = h * 0.5f; + y1 = h * (1.0f - index); + y2 = h * index; + } else { + h2 = h * index; + y1 = y2 = h2; + } + rect.addElement(new Rectangle2D.Float(0, 0, w, h2)); + rect.addElement(new Rectangle2D.Float(0, h2, w, h-h2)); + grad.addElement(new GradientPaint(0,0,c1,0,y1,c2)); + grad.addElement(new GradientPaint(0,y2,c2,0,h,c1)); + } else if ((type & BUR) != 0) { + + float w2 = w/2; + float h2 = h/2; + + rect.addElement(new Rectangle2D.Float(0, 0, w2, h2)); + rect.addElement(new Rectangle2D.Float(w2, 0, w2, h2)); + rect.addElement(new Rectangle2D.Float(0, h2, w2, h2)); + rect.addElement(new Rectangle2D.Float(w2, h2, w2, h2)); + + float x1 = w * (1.0f - index); + float x2 = w * index; + float y1 = h * (1.0f - index); + float y2 = h * index; + + grad.addElement(new GradientPaint(0,0,c1,x1,y1,c2)); + grad.addElement(new GradientPaint(w,0,c1,x2,y1,c2)); + grad.addElement(new GradientPaint(0,h,c1,x1,y2,c2)); + grad.addElement(new GradientPaint(w,h,c1,x2,y2,c2)); + } else if ((type & NF) != 0) { + float x = w * index; + float y = h * index; + grad.addElement(new GradientPaint(0,0,c1,0,y,c2)); + } + + if ((type & INC) != 0 || (type & DEC) != 0) { + index += incr; + } + } + + + public void render(int w, int h, Graphics2D g2) { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + for (int i = 0; i < grad.size(); i++) { + g2.setPaint((GradientPaint) grad.get(i)); + if ((type & NF) == 0) { + g2.fill((Rectangle2D) rect.get(i)); + } + } + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End GpE class + + + + /** + * TexturePaint Effect. Expand and collapse a texture. + */ + static class TpE implements Part { + + static final int INC = 1; // increasing + static final int DEC = 2; // decreasing + static final int OVAL = 4; // oval + static final int RECT = 8; // rectangle + static final int HAF = 16; // half oval or rect size + static final int OI = OVAL | INC; + static final int OD = OVAL | DEC; + static final int RI = RECT | INC; + static final int RD = RECT | DEC; + static final int NF = 32; // no fill + private Paint p1, p2; + private int beginning, ending; + private float incr, index; + private TexturePaint texture; + private int type; + private int size; + private BufferedImage bimg; + private Rectangle rect; + + + public TpE(int type, Paint p1, Paint p2, int size, + int beg, int end) { + this.type = type; + this.p1 = p1; + this.p2 = p2; + this.beginning = beg; + this.ending = end; + setTextureSize(size); + } + + + public void setTextureSize(int size) { + this.size = size; + bimg = new BufferedImage(size,size,BufferedImage.TYPE_INT_RGB); + rect = new Rectangle(0,0,size,size); + } + + + public void reset(int w, int h) { + incr = (float) (size) / (float) (ending - beginning); + if ((type & HAF) != 0) { + incr /= 2; + } + if ((type & DEC) != 0) { + index = size; + if ((type & HAF) != 0) { + index /= 2; + } + incr = -incr; + } else { + index = 0.0f; + } + index += incr; + } + + + public void step(int w, int h) { + Graphics2D g2 = bimg.createGraphics(); + g2.setPaint(p1); + g2.fillRect(0,0,size,size); + g2.setPaint(p2); + if ((type & OVAL) != 0) { + g2.fill(new Ellipse2D.Float(0,0,index,index)); + } else if ((type & RECT) != 0) { + g2.fill(new Rectangle2D.Float(0,0,index,index)); + } + texture = new TexturePaint(bimg, rect); + g2.dispose(); + index += incr; + } + + + public void render(int w, int h, Graphics2D g2) { + g2.setPaint(texture); + if ((type & NF) == 0) { + g2.fillRect(0, 0, w, h); + } + } + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End TpE class + + + + /** + * Close out effect. Close out the buffered image with different + * geometry shapes. + */ + static class CoE implements Part { + + static final int WID = 1; + static final int HEI = 2; + static final int OVAL = 4; + static final int RECT = 8; + static final int RAND = 16; + static final int ARC = 32; + private int type; + private int beginning, ending; + private BufferedImage bimg; + private Shape shape; + private double zoom, extent; + private double zIncr, eIncr; + private boolean doRandom; + + + public CoE(int type, int beg, int end) { + this.type = type; + this.beginning = beg; + this.ending = end; + zIncr = -(2.0 / (ending - beginning)); + eIncr = 360.0 / (ending - beginning); + doRandom = (type & RAND) != 0; + } + + + public void reset(int w, int h) { + if (doRandom) { + int num = (int) (Math.random() * 5.0); + switch (num) { + case 0 : type = OVAL; break; + case 1 : type = RECT; break; + case 2 : type = RECT | WID; break; + case 3 : type = RECT | HEI; break; + case 4 : type = ARC; break; + default : type = OVAL; + } + } + shape = null; + bimg = null; + extent = 360.0; + zoom = 2.0; + } + + + public void step(int w, int h) { + if (bimg == null) { + int biw = Surface.bimg.getWidth(); + int bih = Surface.bimg.getHeight(); + bimg = new BufferedImage(biw, bih, BufferedImage.TYPE_INT_RGB); + Graphics2D big = bimg.createGraphics(); + big.drawImage(Surface.bimg, 0, 0, null); + } + double z = Math.min(w, h) * zoom; + if ((type & OVAL) != 0) { + shape = new Ellipse2D.Double(w/2-z/2,h/2-z/2,z,z); + } else if ((type & ARC) != 0) { + shape = new Arc2D.Double(-100,-100,w+200,h+200,90,extent,Arc2D.PIE); + extent -= eIncr; + } else if ((type & RECT) != 0) { + if ((type & WID) != 0) { + shape = new Rectangle2D.Double(w/2-z/2,0,z,h); + } else if ((type & HEI) != 0) { + shape = new Rectangle2D.Double(0,h/2-z/2,w,z); + } else { + shape = new Rectangle2D.Double(w/2-z/2,h/2-z/2,z,z); + } + } + zoom += zIncr; + } + + + public void render(int w, int h, Graphics2D g2) { + g2.clip(shape); + g2.drawImage(bimg, 0, 0, null); + } + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End CoE class + + + + /** + * Dither Dissolve Effect. For each successive step in the animation, + * a pseudo-random starting horizontal position is chosen using list, + * and then the corresponding points created from xlist and ylist are + * blacked out for the current "chunk". The x and y chunk starting + * positions are each incremented by the associated chunk size, and + * this process is repeated for the number of "steps" in the + * animation, causing an equal number of pseudo-randomly picked + * "blocks" to be blacked out during each step of the animation. + */ + static class DdE implements Part { + + private int beginning, ending; + private BufferedImage bimg; + private Graphics2D big; + private List list, xlist, ylist; + private int xeNum, yeNum; // element number + private int xcSize, ycSize; // chunk size + private int inc; + private int blocksize; + + + public DdE(int beg, int end, int blocksize) { + this.beginning = beg; + this.ending = end; + this.blocksize = blocksize; + } + + private void createShuffledLists() { + int width = bimg.getWidth(); + int height = bimg.getHeight(); + Integer xarray[] = new Integer[width]; + Integer yarray[] = new Integer[height]; + Integer array[] = new Integer[ending - beginning + 1]; + for (int i = 0; i < xarray.length; i++) { + xarray[i] = new Integer(i); + } + for (int j = 0; j < yarray.length; j++) { + yarray[j] = new Integer(j); + } + for (int k = 0; k < array.length; k++) { + array[k] = new Integer(k); + } + java.util.Collections.shuffle(xlist = Arrays.asList(xarray)); + java.util.Collections.shuffle(ylist = Arrays.asList(yarray)); + java.util.Collections.shuffle(list = Arrays.asList(array)); + } + + public void reset(int w, int h) { + bimg = null; + } + + public void step(int w, int h) { + if (bimg == null) { + int biw = Surface.bimg.getWidth(); + int bih = Surface.bimg.getHeight(); + bimg = new BufferedImage(biw, bih, BufferedImage.TYPE_INT_RGB); + createShuffledLists(); + big = bimg.createGraphics(); + big.drawImage(Surface.bimg, 0, 0, null); + xcSize = (xlist.size() / (ending - beginning)) + 1; + ycSize = (ylist.size() / (ending - beginning)) + 1; + xeNum = 0; + inc = 0; + } + xeNum = xcSize * ((Integer)list.get(inc)).intValue(); + yeNum = -ycSize; + inc++; + } + + + public void render(int w, int h, Graphics2D g2) { + big.setColor(black); + + for (int k = 0; k <= (ending - beginning); k++) { + if ((xeNum + xcSize) > xlist.size()) { + xeNum = 0; + } else { + xeNum += xcSize; + } + yeNum += ycSize; + + for (int i = xeNum; i < xeNum+xcSize && i < xlist.size(); i++) { + for (int j = yeNum; j < yeNum+ycSize && j < ylist.size(); j++) { + int xval = ((Integer)xlist.get(i)).intValue(); + int yval = ((Integer)ylist.get(j)).intValue(); + if (((xval % blocksize) == 0) && + ((yval % blocksize) == 0)) { + big.fillRect(xval, yval, blocksize, blocksize); + } + } + } + } + + g2.drawImage(bimg, 0, 0, null); + } + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End DdE class + + + /** + * Subimage effect. Subimage the scene's buffered + * image then rotate and scale down the subimages. + */ + static class SiE implements Part { + + private int beginning, ending; + private BufferedImage bimg; + private double rIncr, sIncr; + private double scale, rotate; + private int siw, sih; + private Vector subs = new Vector(20); + private Vector pts = new Vector(20); + + + public SiE(int siw, int sih, int beg, int end) { + this.siw = siw; + this.sih = sih; + this.beginning = beg; + this.ending = end; + rIncr = 360.0 / (ending - beginning); + sIncr = 1.0 / (ending - beginning); + } + + + public void reset(int w, int h) { + scale = 1.0; + rotate = 0.0; + bimg = null; + subs.clear(); + pts.clear(); + } + + + public void step(int w, int h) { + if (bimg == null) { + int biw = Surface.bimg.getWidth(); + int bih = Surface.bimg.getHeight(); + bimg = new BufferedImage(biw, bih, BufferedImage.TYPE_INT_RGB); + Graphics2D big = bimg.createGraphics(); + big.drawImage(Surface.bimg, 0, 0, null); + for (int x = 0; x < w && scale > 0.0; x+=siw) { + int ww = x+siw < w ? siw : w-x; + for (int y = 0; y < h; y+=sih) { + int hh = y+sih < h ? sih : h-y; + subs.addElement(bimg.getSubimage(x,y,ww,hh)); + pts.addElement(new Point(x, y)); + } + } + } + + rotate += rIncr; + scale -= sIncr; + } + + + public void render(int w, int h, Graphics2D g2) { + AffineTransform saveTx = g2.getTransform(); + g2.setColor(blue); + for (int i = 0; i < subs.size() && scale > 0.0; i++) { + BufferedImage bi = (BufferedImage) subs.get(i); + Point p = (Point) pts.get(i); + int ww = bi.getWidth(); + int hh = bi.getHeight(); + AffineTransform at = new AffineTransform(); + at.rotate(Math.toRadians(rotate), p.x+ww/2, p.y+hh/2); + at.translate(p.x, p.y); + at.scale(scale, scale); + + Rectangle b1 = new Rectangle(0, 0, ww, hh); + Shape shape = at.createTransformedShape(b1); + Rectangle2D b2 = shape.getBounds2D(); + double xx = (p.x+ww/2) - (b2.getX()+b2.getWidth()/2); + double yy = (p.y+hh/2) - (b2.getY()+b2.getHeight()/2); + AffineTransform toCenterAT = new AffineTransform(); + toCenterAT.translate(xx, yy); + toCenterAT.concatenate(at); + + g2.setTransform(toCenterAT); + g2.drawImage(bi, 0, 0, null); + g2.draw(b1); + } + g2.setTransform(saveTx); + } + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End SiE class + + + + + /** + * Line Effect. Flattened ellipse with lines from the center + * to the edge. Expand or collapse the ellipse. Fade in or out + * the lines. + */ + static class LnE implements Part { + + static final int INC = 1; + static final int DEC = 2; + static final int R = 4; // rotate + static final int RI = R | INC; + static final int RD = R | DEC; + static final int ZOOM = 8; // zoom + static final int ZOOMI = ZOOM | INC; + static final int ZOOMD = ZOOM | DEC; + static final int AC = 32; // AlphaComposite + static final int ACI = 32 | INC; + static final int ACD = 32 | DEC; + private int beginning, ending; + private double rIncr, rotate; + private double zIncr, zoom; + private Vector pts = new Vector(); + private float alpha, aIncr; + private int type; + + + public LnE(int type, int beg, int end) { + this.type = type; + this.beginning = beg; + this.ending = end; + rIncr = 360.0 / (ending - beginning); + aIncr = 0.9f / (ending - beginning); + zIncr = 2.0 / (ending - beginning); + if ((type & DEC) != 0) { + rIncr = -rIncr; + aIncr = -aIncr; + zIncr = -zIncr; + } + } + + + public void generatePts(int w, int h, double sizeF) { + pts.clear(); + double size = Math.min(w, h) * sizeF; + Ellipse2D ellipse = new Ellipse2D.Double(w/2-size/2,h/2-size/2,size,size); + PathIterator pi = ellipse.getPathIterator(null, 0.8); + while ( !pi.isDone() ) { + double[] pt = new double[6]; + switch ( pi.currentSegment(pt) ) { + case FlatteningPathIterator.SEG_MOVETO: + case FlatteningPathIterator.SEG_LINETO: + pts.addElement(new Point2D.Double(pt[0], pt[1])); + } + pi.next(); + } + } + + + public void reset(int w, int h) { + if ((type & DEC) != 0) { + rotate = 360; + alpha = 1.0f; + zoom = 2.0; + } else { + rotate = alpha = 0; + zoom = 0; + } + if ((type & ZOOM) == 0) { + generatePts(w, h, 0.5); + } + } + + + public void step(int w, int h) { + if ((type & ZOOM) != 0) { + generatePts(w, h, zoom += zIncr); + } + if ((type & RI) != 0 || (type & RI) != 0) { + rotate += rIncr; + } + if ((type & ACI) != 0 || (type & ACD) != 0) { + alpha += aIncr; + } + } + + + public void render(int w, int h, Graphics2D g2) { + Composite saveAC = null; + if ((type & AC) != 0 && alpha >= 0 && alpha <= 1) { + saveAC = g2.getComposite(); + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); + } + AffineTransform saveTx = null; + if ((type & R) != 0) { + saveTx = g2.getTransform(); + AffineTransform at = new AffineTransform(); + at.rotate(Math.toRadians(rotate), w/2, h/2); + g2.setTransform(at); + } + Point2D p1 = new Point2D.Double(w/2, h/2); + g2.setColor(Color.yellow); + for (int i = 0; i < pts.size()-1; i++) { + g2.draw(new Line2D.Float(p1, (Point2D) pts.get(i))); + } + if (saveTx != null) { + g2.setTransform(saveTx); + } + if (saveAC != null) { + g2.setComposite(saveAC); + } + } + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End LnE class + + + + /** + * Template for Features & Contributors consisting of translating + * blue and red rectangles and an image going from transparent to + * opaque. + */ + static class Temp implements Part { + static final int NOANIM = 1; + static final int RECT = 2; + static final int RNA = RECT | NOANIM; + static final int IMG = 4; + static final int INA = IMG | NOANIM; + private int beginning, ending; + private float alpha, aIncr; + private int type; + private Rectangle rect1, rect2; + private int x, y, xIncr, yIncr; + private Image img; + + + public Temp(int type, Image img, int beg, int end) { + this.type = type; + this.img = img; + this.beginning = beg; + this.ending = end; + aIncr = 0.9f / (ending - beginning); + if ((type & NOANIM) != 0) { + alpha = 1.0f; + } + } + + + + public void reset(int w, int h) { + rect1 = new Rectangle(8, 20, w-20, 30); + rect2 = new Rectangle(20, 8, 30, h-20); + if ((type & NOANIM) == 0) { + alpha = 0.0f; + xIncr = w / (ending - beginning); + yIncr = h / (ending - beginning); + x = w+(int)(xIncr*1.4); + y = h+(int)(yIncr*1.4); + } + } + + + public void step(int w, int h) { + if ((type & NOANIM) != 0) { + return; + } + if ((type & RECT) != 0) { + rect1.setLocation(x-=xIncr, 20); + rect2.setLocation(20, y-=yIncr); + } + if ((type & IMG) != 0) { + alpha += aIncr; + } + } + + + public void render(int w, int h, Graphics2D g2) { + if ((type & RECT) != 0) { + g2.setColor(blue); + g2.fill(rect1); + g2.setColor(red); + g2.fill(rect2); + } + if ((type & IMG) != 0) { + Composite saveAC = g2.getComposite(); + if (alpha >= 0 && alpha <= 1) { + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); + } + g2.drawImage(img, 30, 30, null); + g2.setComposite(saveAC); + } + } + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End Temp class + + + + /** + * Features of Java2D. Single character advancement effect. + */ + static class Features implements Part { + + static final int GRAPHICS = 0; + static final int TEXT = 1; + static final int IMAGES = 2; + static final int COLOR = 3; + static Font font1 = new Font("serif", Font.BOLD, 38); + static Font font2 = new Font("serif", Font.PLAIN, 24); + static FontMetrics fm1 = Surface.getMetrics(font1); + static FontMetrics fm2 = Surface.getMetrics(font2); + static String table[][] = + {{ "Graphics", "Antialiased rendering", "Bezier paths", + "Transforms", "Compositing", "Stroking parameters" }, + { "Text", "Extended font support", + "Advanced text layout", "Dynamic font loading", + "AttributeSets for font customization" }, + { "Images", "Flexible image layouts", + "Extended imaging operations", + " Convolutions, Lookup Tables", + "RenderableImage interface"}, + { "Color", "ICC profile support", "Color conversion", + "Arbitrary color spaces"} }; + private String list[]; + private int beginning, ending; + private int strH; + private int endIndex, listIndex; + private Vector v = new Vector(); + + + public Features(int type, int beg, int end) { + list = table[type]; + this.beginning = beg; + this.ending = end; + } + + + public void reset(int w, int h) { + strH = (int) (fm2.getAscent()+fm2.getDescent()); + endIndex = 1; + listIndex = 0; + v.clear(); + v.addElement(list[listIndex].substring(0,endIndex)); + } + + + public void step(int w, int h) { + if (listIndex < list.length) { + if (++endIndex > list[listIndex].length()) { + if (++listIndex < list.length) { + endIndex = 1; + v.addElement(list[listIndex].substring(0,endIndex)); + } + } else { + v.set(listIndex, list[listIndex].substring(0,endIndex)); + } + } + } + + + public void render(int w, int h, Graphics2D g2) { + g2.setColor(white); + g2.setFont(font1); + g2.drawString((String) v.get(0), 90, 85); + g2.setFont(font2); + for (int i = 1, y = 90; i < v.size(); i++) { + g2.drawString((String) v.get(i), 120, y += strH); + } + } + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End Features class + + + + /** + * Scrolling text of Java2D contributors. + */ + static class Contributors implements Part { + + static String members[] = + { + "Brian Lichtenwalter", "Jeannette Hung", + "Thanh Nguyen", "Jim Graham", "Jerry Evans", + "John Raley", "Michael Peirce", "Robert Kim", + "Jennifer Ball", "Deborah Adair", "Paul Charlton", + "Dmitry Feld", "Gregory Stone", "Richard Blanchard", + "Link Perry", "Phil Race", "Vincent Hardy", + "Parry Kejriwal", "Doug Felt", "Rekha Rangarajan", + "Paula Patel", "Michael Bundschuh", "Joe Warzecha", + "Joey Beheler", "Aastha Bhardwaj", "Daniel Rice", + "Chris Campbell", "Shinsuke Fukuda", "Dmitri Trembovetski", + "Chet Haase", "Jennifer Godinez", "Nicholas Talian", + "Raul Vera", "Ankit Patel", "Ilya Bagrak" + }; + static Font font = new Font("serif", Font.PLAIN, 26); + static FontMetrics fm = Surface.getMetrics(font); + private int beginning, ending; + private int nStrs, strH, index, yh, height; + private Vector v = new Vector(); + private Vector cast = new Vector(members.length+3); + private int counter, cntMod; + private GradientPaint gp; + + + public Contributors(int beg, int end) { + this.beginning = beg; + this.ending = end; + java.util.Arrays.sort(members); + cast.addElement("CONTRIBUTORS"); + cast.addElement(" "); + for (int i = 0; i < members.length; i++) { + cast.addElement(members[i]); + } + cast.addElement(" "); cast.addElement(" "); + cntMod = (ending - beginning) / cast.size() - 1; + } + + + public void reset(int w, int h) { + v.clear(); + strH = (int) (fm.getAscent()+fm.getDescent()); + nStrs = (h-40)/strH + 1; + height = strH * (nStrs-1) + 48; + index = 0; + gp = new GradientPaint(0,h/2,Color.white,0,h+20,Color.black); + counter = 0; + } + + + public void step(int w, int h) { + if (counter++%cntMod == 0) { + if (index < cast.size()) { + v.addElement(cast.get(index)); + } + if ((v.size() == nStrs || index >= cast.size()) && v.size() != 0) { + v.removeElementAt(0); + } + ++index; + } + } + + + public void render(int w, int h, Graphics2D g2) { + g2.setPaint(gp); + g2.setFont(font); + double remainder = counter%cntMod; + double incr = 1.0-remainder/cntMod; + incr = incr == 1.0 ? 0 : incr; + int y = (int) (incr * strH); + + if (index >= cast.size()) { + y = yh + y; + } else { + y = yh = height - v.size() * strH + y; + } + for (int i = 0; i < v.size(); i++) { + String s = (String) v.get(i); + g2.drawString(s, w/2-fm.stringWidth(s)/2, y += strH); + } + } + + public int getBegin() { + return beginning; + } + + public int getEnd() { + return ending; + } + } // End Contributors class + + } // End Surface class +} // End Intro class diff --git a/tests/examplefiles/Makefile b/tests/examplefiles/Makefile new file mode 100644 index 0000000..bf272c5 --- /dev/null +++ b/tests/examplefiles/Makefile @@ -0,0 +1,1131 @@ +# Generated automatically from Makefile.pre by makesetup. +# Top-level Makefile for Python +# +# As distributed, this file is called Makefile.pre.in; it is processed +# into the real Makefile by running the script ./configure, which +# replaces things like @spam@ with values appropriate for your system. +# This means that if you edit Makefile, your changes get lost the next +# time you run the configure script. Ideally, you can do: +# +# ./configure +# make +# make test +# make install +# +# If you have a previous version of Python installed that you don't +# want to overwrite, you can use "make altinstall" instead of "make +# install". Refer to the "Installing" section in the README file for +# additional details. +# +# See also the section "Build instructions" in the README file. + +# === Variables set by makesetup === + +MODOBJS= Modules/threadmodule.o Modules/signalmodule.o Modules/posixmodule.o Modules/errnomodule.o Modules/pwdmodule.o Modules/_sre.o Modules/_codecsmodule.o Modules/zipimport.o Modules/symtablemodule.o Modules/xxsubtype.o +MODLIBS= $(LOCALMODLIBS) $(BASEMODLIBS) + +# === Variables set by configure +VERSION= 2.6 +srcdir= . + + +CC= gcc -pthread +CXX= g++ -pthread +MAINCC= $(CC) +LINKCC= $(PURIFY) $(MAINCC) +AR= ar +RANLIB= ranlib +SVNVERSION= svnversion $(srcdir) + +# Shell used by make (some versions default to the login shell, which is bad) +SHELL= /bin/sh + +# Use this to make a link between python$(VERSION) and python in $(BINDIR) +LN= ln + +# Portable install script (configure doesn't always guess right) +INSTALL= /usr/bin/install -c +INSTALL_PROGRAM=${INSTALL} +INSTALL_SCRIPT= ${INSTALL} +INSTALL_DATA= ${INSTALL} -m 644 +# Shared libraries must be installed with executable mode on some systems; +# rather than figuring out exactly which, we always give them executable mode. +# Also, making them read-only seems to be a good idea... +INSTALL_SHARED= ${INSTALL} -m 555 + +MAKESETUP= $(srcdir)/Modules/makesetup + +# Compiler options +OPT= -g -Wall -Wstrict-prototypes +BASECFLAGS= -fno-strict-aliasing +CFLAGS= $(BASECFLAGS) $(OPT) $(EXTRA_CFLAGS) +# Both CPPFLAGS and LDFLAGS need to contain the shell's value for setup.py to +# be able to build extension modules using the directories specified in the +# environment variables +CPPFLAGS= -I. -I$(srcdir)/Include +LDFLAGS= +LDLAST= +SGI_ABI= +CCSHARED= -fPIC +LINKFORSHARED= -Xlinker -export-dynamic +# Extra C flags added for building the interpreter object files. +CFLAGSFORSHARED= +# C flags used for building the interpreter object files +PY_CFLAGS= $(CFLAGS) $(CPPFLAGS) $(CFLAGSFORSHARED) -DPy_BUILD_CORE + + +# Machine-dependent subdirectories +MACHDEP= linux2 + +# Install prefix for architecture-independent files +prefix= /usr/local + +# Install prefix for architecture-dependent files +exec_prefix= ${prefix} + +# Expanded directories +BINDIR= $(exec_prefix)/bin +LIBDIR= $(exec_prefix)/lib +MANDIR= ${prefix}/man +INCLUDEDIR= ${prefix}/include +CONFINCLUDEDIR= $(exec_prefix)/include +SCRIPTDIR= $(prefix)/lib + +# Detailed destination directories +BINLIBDEST= $(LIBDIR)/python$(VERSION) +LIBDEST= $(SCRIPTDIR)/python$(VERSION) +INCLUDEPY= $(INCLUDEDIR)/python$(VERSION) +CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(VERSION) +LIBP= $(LIBDIR)/python$(VERSION) + +# Symbols used for using shared libraries +SO= .so +LDSHARED= $(CC) -shared +BLDSHARED= $(CC) -shared +DESTSHARED= $(BINLIBDEST)/lib-dynload + +# Executable suffix (.exe on Windows and Mac OS X) +EXE= +BUILDEXE= + +# Short name and location for Mac OS X Python framework +UNIVERSALSDK= +PYTHONFRAMEWORK= +PYTHONFRAMEWORKDIR= no-framework +PYTHONFRAMEWORKPREFIX= +PYTHONFRAMEWORKINSTALLDIR= +# Deployment target selected during configure, to be checked +# by distutils. The export statement is needed to ensure that the +# deployment target is active during build. +MACOSX_DEPLOYMENT_TARGET= +#export MACOSX_DEPLOYMENT_TARGET + +# Options to enable prebinding (for fast startup prior to Mac OS X 10.3) +OTHER_LIBTOOL_OPT= + +# Environment to run shared python without installed libraries +RUNSHARED= + +# Modes for directories, executables and data files created by the +# install process. Default to user-only-writable for all file types. +DIRMODE= 755 +EXEMODE= 755 +FILEMODE= 644 + +# configure script arguments +CONFIG_ARGS= '--with-pydebug' + + +# Subdirectories with code +SRCDIRS= Parser Grammar Objects Python Modules Mac + +# Other subdirectories +SUBDIRSTOO= Include Lib Misc Demo + +# Files and directories to be distributed +CONFIGFILES= configure configure.in acconfig.h pyconfig.h.in Makefile.pre.in +DISTFILES= README ChangeLog $(CONFIGFILES) +DISTDIRS= $(SUBDIRS) $(SUBDIRSTOO) Ext-dummy +DIST= $(DISTFILES) $(DISTDIRS) + + +LIBRARY= libpython$(VERSION).a +LDLIBRARY= libpython$(VERSION).a +BLDLIBRARY= $(LDLIBRARY) +DLLLIBRARY= +LDLIBRARYDIR= +INSTSONAME= $(LDLIBRARY) + + +LIBS= -lpthread -ldl -lutil +LIBM= -lm +LIBC= +SYSLIBS= $(LIBM) $(LIBC) +SHLIBS= $(LIBS) + +THREADOBJ= Python/thread.o +DLINCLDIR= . +DYNLOADFILE= dynload_shlib.o +MACHDEP_OBJS= +UNICODE_OBJS= Objects/unicodeobject.o Objects/unicodectype.o + +PYTHON= python$(EXE) +BUILDPYTHON= python$(BUILDEXE) + +# === Definitions added by makesetup === + +LOCALMODLIBS= +BASEMODLIBS= +GLHACK=-Dclear=__GLclear +PYTHONPATH=$(COREPYTHONPATH) +COREPYTHONPATH=$(DESTPATH)$(SITEPATH)$(TESTPATH)$(MACHDEPPATH)$(EXTRAMACHDEPPATH)$(TKPATH) +TKPATH=:lib-tk +EXTRAMACHDEPPATH= +MACHDEPPATH=:plat-$(MACHDEP) +TESTPATH= +SITEPATH= +DESTPATH= +MACHDESTLIB=$(BINLIBDEST) +DESTLIB=$(LIBDEST) + + + +########################################################################## +# Modules +MODULE_OBJS= \ + Modules/config.o \ + Modules/getpath.o \ + Modules/main.o \ + Modules/gcmodule.o + +# Used of signalmodule.o is not available +SIGNAL_OBJS= + + +########################################################################## +# Grammar +GRAMMAR_H= $(srcdir)/Include/graminit.h +GRAMMAR_C= $(srcdir)/Python/graminit.c +GRAMMAR_INPUT= $(srcdir)/Grammar/Grammar + + +########################################################################## +# Parser +PGEN= Parser/pgen$(EXE) + +POBJS= \ + Parser/acceler.o \ + Parser/grammar1.o \ + Parser/listnode.o \ + Parser/node.o \ + Parser/parser.o \ + Parser/parsetok.o \ + Parser/bitset.o \ + Parser/metagrammar.o \ + Parser/firstsets.o \ + Parser/grammar.o \ + Parser/pgen.o + +PARSER_OBJS= $(POBJS) Parser/myreadline.o Parser/tokenizer.o + +PGOBJS= \ + Objects/obmalloc.o \ + Python/mysnprintf.o \ + Parser/tokenizer_pgen.o \ + Parser/printgrammar.o \ + Parser/pgenmain.o + +PGENOBJS= $(PGENMAIN) $(POBJS) $(PGOBJS) + +########################################################################## +# AST +AST_H_DIR= $(srcdir)/Include +AST_H= $(AST_H_DIR)/Python-ast.h +AST_C_DIR= $(srcdir)/Python +AST_C= $(AST_C_DIR)/Python-ast.c +AST_ASDL= $(srcdir)/Parser/Python.asdl + +ASDLGEN_FILES= $(srcdir)/Parser/asdl.py $(srcdir)/Parser/asdl_c.py +# XXX Note that a build now requires Python exist before the build starts +ASDLGEN= $(srcdir)/Parser/asdl_c.py + +########################################################################## +# Python +PYTHON_OBJS= \ + Python/Python-ast.o \ + Python/asdl.o \ + Python/ast.o \ + Python/bltinmodule.o \ + Python/ceval.o \ + Python/compile.o \ + Python/codecs.o \ + Python/errors.o \ + Python/frozen.o \ + Python/frozenmain.o \ + Python/future.o \ + Python/getargs.o \ + Python/getcompiler.o \ + Python/getcopyright.o \ + Python/getmtime.o \ + Python/getplatform.o \ + Python/getversion.o \ + Python/graminit.o \ + Python/import.o \ + Python/importdl.o \ + Python/marshal.o \ + Python/modsupport.o \ + Python/mystrtoul.o \ + Python/mysnprintf.o \ + Python/peephole.o \ + Python/pyarena.o \ + Python/pyfpe.o \ + Python/pystate.o \ + Python/pythonrun.o \ + Python/structmember.o \ + Python/symtable.o \ + Python/sysmodule.o \ + Python/traceback.o \ + Python/getopt.o \ + Python/pystrtod.o \ + Python/$(DYNLOADFILE) \ + $(MACHDEP_OBJS) \ + $(THREADOBJ) + + +########################################################################## +# Objects +OBJECT_OBJS= \ + Objects/abstract.o \ + Objects/boolobject.o \ + Objects/bufferobject.o \ + Objects/cellobject.o \ + Objects/classobject.o \ + Objects/cobject.o \ + Objects/codeobject.o \ + Objects/complexobject.o \ + Objects/descrobject.o \ + Objects/enumobject.o \ + Objects/exceptions.o \ + Objects/genobject.o \ + Objects/fileobject.o \ + Objects/floatobject.o \ + Objects/frameobject.o \ + Objects/funcobject.o \ + Objects/intobject.o \ + Objects/iterobject.o \ + Objects/listobject.o \ + Objects/longobject.o \ + Objects/dictobject.o \ + Objects/methodobject.o \ + Objects/moduleobject.o \ + Objects/object.o \ + Objects/obmalloc.o \ + Objects/rangeobject.o \ + Objects/setobject.o \ + Objects/sliceobject.o \ + Objects/stringobject.o \ + Objects/structseq.o \ + Objects/tupleobject.o \ + Objects/typeobject.o \ + Objects/weakrefobject.o \ + $(UNICODE_OBJS) + + +########################################################################## +# objects that get linked into the Python library +LIBRARY_OBJS= \ + Modules/_typesmodule.o \ + Modules/getbuildinfo.o \ + $(PARSER_OBJS) \ + $(OBJECT_OBJS) \ + $(PYTHON_OBJS) \ + $(MODULE_OBJS) \ + $(SIGNAL_OBJS) \ + $(MODOBJS) + +######################################################################### +# Rules + +# Default target +all: $(BUILDPYTHON) oldsharedmods sharedmods + +# Build the interpreter +$(BUILDPYTHON): Modules/python.o $(LIBRARY) $(LDLIBRARY) + $(LINKCC) $(LDFLAGS) $(LINKFORSHARED) -o $@ \ + Modules/python.o \ + $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) + +platform: $(BUILDPYTHON) + $(RUNSHARED) ./$(BUILDPYTHON) -E -c 'import sys ; from distutils.util import get_platform ; print get_platform()+"-"+sys.version[0:3]' >platform + + +# Build the shared modules +sharedmods: $(BUILDPYTHON) + @case $$MAKEFLAGS in \ + *-s*) $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' ./$(BUILDPYTHON) -E $(srcdir)/setup.py -q build;; \ + *) $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' ./$(BUILDPYTHON) -E $(srcdir)/setup.py build;; \ + esac + +# Build static library +# avoid long command lines, same as LIBRARY_OBJS +$(LIBRARY): $(LIBRARY_OBJS) + -rm -f $@ + $(AR) cr $@ Modules/getbuildinfo.o + $(AR) cr $@ Modules/_typesmodule.o + $(AR) cr $@ $(PARSER_OBJS) + $(AR) cr $@ $(OBJECT_OBJS) + $(AR) cr $@ $(PYTHON_OBJS) + $(AR) cr $@ $(MODULE_OBJS) $(SIGNAL_OBJS) + $(AR) cr $@ $(MODOBJS) + $(RANLIB) $@ + +libpython$(VERSION).so: $(LIBRARY_OBJS) + if test $(INSTSONAME) != $(LDLIBRARY); then \ + $(LDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ + $(LN) -f $(INSTSONAME) $@; \ + else\ + $(LDSHARED) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ + fi + +libpython$(VERSION).sl: $(LIBRARY_OBJS) + $(LDSHARED) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM) + +# This rule is here for OPENSTEP/Rhapsody/MacOSX. It builds a temporary +# minimal framework (not including the Lib directory and such) in the current +# directory. +RESSRCDIR=$(srcdir)/Mac/Resources/framework +$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ + $(LIBRARY) \ + $(RESSRCDIR)/Info.plist \ + $(RESSRCDIR)/version.plist \ + $(RESSRCDIR)/English.lproj/InfoPlist.strings + $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION) + if test "${UNIVERSALSDK}"; then \ + $(CC) -o $(LDLIBRARY) -arch i386 -arch ppc -dynamiclib \ + -isysroot "${UNIVERSALSDK}" \ + -all_load $(LIBRARY) -Wl,-single_module \ + -install_name $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/Python \ + -compatibility_version $(VERSION) \ + -current_version $(VERSION); \ + else \ + libtool -o $(LDLIBRARY) -dynamic $(OTHER_LIBTOOL_OPT) $(LIBRARY) \ + ;\ + fi + $(INSTALL) -d -m $(DIRMODE) \ + $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/Resources/English.lproj + $(INSTALL_DATA) $(RESSRCDIR)/Info.plist \ + $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/Resources/Info.plist + $(INSTALL_DATA) $(RESSRCDIR)/version.plist \ + $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/Resources/version.plist + $(INSTALL_DATA) $(RESSRCDIR)/English.lproj/InfoPlist.strings \ + $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/Resources/English.lproj/InfoPlist.strings + $(LN) -fsn $(VERSION) $(PYTHONFRAMEWORKDIR)/Versions/Current + $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK) + $(LN) -fsn Versions/Current/Headers $(PYTHONFRAMEWORKDIR)/Headers + $(LN) -fsn Versions/Current/Resources $(PYTHONFRAMEWORKDIR)/Resources + +# This rule builds the Cygwin Python DLL and import library if configured +# for a shared core library; otherwise, this rule is a noop. +$(DLLLIBRARY) libpython$(VERSION).dll.a: $(LIBRARY_OBJS) + if test -n "$(DLLLIBRARY)"; then \ + $(LDSHARED) -Wl,--out-implib=$@ -o $(DLLLIBRARY) $^ \ + $(LIBS) $(MODLIBS) $(SYSLIBS); \ + else true; \ + fi + + +oldsharedmods: $(SHAREDMODS) + + +Makefile Modules/config.c: Makefile.pre \ + $(srcdir)/Modules/config.c.in \ + $(MAKESETUP) \ + Modules/Setup.config \ + Modules/Setup \ + Modules/Setup.local + $(SHELL) $(MAKESETUP) -c $(srcdir)/Modules/config.c.in \ + -s Modules \ + Modules/Setup.config \ + Modules/Setup.local \ + Modules/Setup + @mv config.c Modules + @echo "The Makefile was updated, you may need to re-run make." + + +Modules/Setup: $(srcdir)/Modules/Setup.dist + @if test -f Modules/Setup; then \ + echo "-----------------------------------------------"; \ + echo "Modules/Setup.dist is newer than Modules/Setup;"; \ + echo "check to make sure you have all the updates you"; \ + echo "need in your Modules/Setup file."; \ + echo "Usually, copying Setup.dist to Setup will work."; \ + echo "-----------------------------------------------"; \ + fi + +############################################################################ +# Special rules for object files + +Modules/getbuildinfo.o: $(PARSER_OBJS) \ + $(OBJECT_OBJS) \ + $(PYTHON_OBJS) \ + $(MODULE_OBJS) \ + $(SIGNAL_OBJS) \ + $(MODOBJS) \ + $(srcdir)/Modules/getbuildinfo.c + $(CC) -c $(PY_CFLAGS) -DSVNVERSION=\"`LC_ALL=C $(SVNVERSION)`\" -o $@ $(srcdir)/Modules/getbuildinfo.c + +Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile + $(CC) -c $(PY_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \ + -DPREFIX='"$(prefix)"' \ + -DEXEC_PREFIX='"$(exec_prefix)"' \ + -DVERSION='"$(VERSION)"' \ + -DVPATH='"$(VPATH)"' \ + -o $@ $(srcdir)/Modules/getpath.c + +Modules/python.o: $(srcdir)/Modules/python.c + $(MAINCC) -c $(PY_CFLAGS) -o $@ $(srcdir)/Modules/python.c + + +$(GRAMMAR_H) $(GRAMMAR_C): $(PGEN) $(GRAMMAR_INPUT) + -$(PGEN) $(GRAMMAR_INPUT) $(GRAMMAR_H) $(GRAMMAR_C) + +$(PGEN): $(PGENOBJS) + $(CC) $(OPT) $(LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN) + +Parser/grammar.o: $(srcdir)/Parser/grammar.c \ + $(srcdir)/Include/token.h \ + $(srcdir)/Include/grammar.h +Parser/metagrammar.o: $(srcdir)/Parser/metagrammar.c + +Parser/tokenizer_pgen.o: $(srcdir)/Parser/tokenizer.c + +Parser/pgenmain.o: $(srcdir)/Include/parsetok.h + +$(AST_H): $(AST_ASDL) $(ASDLGEN_FILES) + $(ASDLGEN) -h $(AST_H_DIR) $(AST_ASDL) + +$(AST_C): $(AST_ASDL) $(ASDLGEN_FILES) + $(ASDLGEN) -c $(AST_C_DIR) $(AST_ASDL) + +Python/compile.o Python/symtable.o: $(GRAMMAR_H) $(AST_H) + +Python/getplatform.o: $(srcdir)/Python/getplatform.c + $(CC) -c $(PY_CFLAGS) -DPLATFORM='"$(MACHDEP)"' -o $@ $(srcdir)/Python/getplatform.c + +Python/importdl.o: $(srcdir)/Python/importdl.c + $(CC) -c $(PY_CFLAGS) -I$(DLINCLDIR) -o $@ $(srcdir)/Python/importdl.c + +Objects/unicodectype.o: $(srcdir)/Objects/unicodectype.c \ + $(srcdir)/Objects/unicodetype_db.h + +############################################################################ +# Header files + +PYTHON_HEADERS= \ + Include/Python.h \ + Include/Python-ast.h \ + Include/asdl.h \ + Include/abstract.h \ + Include/boolobject.h \ + Include/bufferobject.h \ + Include/ceval.h \ + Include/classobject.h \ + Include/cobject.h \ + Include/code.h \ + Include/codecs.h \ + Include/compile.h \ + Include/complexobject.h \ + Include/descrobject.h \ + Include/dictobject.h \ + Include/enumobject.h \ + Include/genobject.h \ + Include/fileobject.h \ + Include/floatobject.h \ + Include/funcobject.h \ + Include/import.h \ + Include/intobject.h \ + Include/intrcheck.h \ + Include/iterobject.h \ + Include/listobject.h \ + Include/longobject.h \ + Include/methodobject.h \ + Include/modsupport.h \ + Include/moduleobject.h \ + Include/object.h \ + Include/objimpl.h \ + Include/parsetok.h \ + Include/patchlevel.h \ + Include/pyarena.h \ + Include/pydebug.h \ + Include/pyerrors.h \ + Include/pyfpe.h \ + Include/pymem.h \ + Include/pyport.h \ + Include/pystate.h \ + Include/pythonrun.h \ + Include/rangeobject.h \ + Include/setobject.h \ + Include/sliceobject.h \ + Include/stringobject.h \ + Include/structseq.h \ + Include/structmember.h \ + Include/symtable.h \ + Include/sysmodule.h \ + Include/traceback.h \ + Include/tupleobject.h \ + Include/unicodeobject.h \ + Include/weakrefobject.h \ + pyconfig.h + +$(LIBRARY_OBJS) $(MODOBJS) Modules/python.o: $(PYTHON_HEADERS) + + +###################################################################### + +# Test the interpreter (twice, once without .pyc files, once with) +# In the past, we've had problems where bugs in the marshalling or +# elsewhere caused bytecode read from .pyc files to behave differently +# than bytecode generated directly from a .py source file. Sometimes +# the bytecode read from a .pyc file had the bug, somtimes the directly +# generated bytecode. This is sometimes a very shy bug needing a lot of +# sample data. + +TESTOPTS= -l $(EXTRATESTOPTS) +TESTPROG= $(srcdir)/Lib/test/regrtest.py +TESTPYTHON= $(RUNSHARED) ./$(BUILDPYTHON) -E -tt +test: all platform + -find $(srcdir)/Lib -name '*.py[co]' -print | xargs rm -f + -$(TESTPYTHON) $(TESTPROG) $(TESTOPTS) + $(TESTPYTHON) $(TESTPROG) $(TESTOPTS) + +testall: all platform + -find $(srcdir)/Lib -name '*.py[co]' -print | xargs rm -f + -$(TESTPYTHON) $(TESTPROG) $(TESTOPTS) -uall + $(TESTPYTHON) $(TESTPROG) $(TESTOPTS) -uall + +# Run the unitests for both architectures in a Universal build on OSX +# Must be run on an Intel box. +testuniversal: all platform + if [ `arch` != 'i386' ];then \ + echo "This can only be used on OSX/i386" ;\ + exit 1 ;\ + fi + -find $(srcdir)/Lib -name '*.py[co]' -print | xargs rm -f + -$(TESTPYTHON) $(TESTPROG) $(TESTOPTS) -uall + $(TESTPYTHON) $(TESTPROG) $(TESTOPTS) -uall + $(RUNSHARED) /usr/libexec/oah/translate ./$(BUILDPYTHON) -E -tt $(TESTPROG) $(TESTOPTS) -uall + + +# Like testall, but with a single pass only +buildbottest: all platform + $(TESTPYTHON) $(TESTPROG) $(TESTOPTS) -uall -rw + +QUICKTESTOPTS= $(TESTOPTS) -x test_thread test_signal test_strftime \ + test_unicodedata test_re test_sre test_select test_poll \ + test_linuxaudiodev test_struct test_sunaudiodev test_zlib +quicktest: all platform + -find $(srcdir)/Lib -name '*.py[co]' -print | xargs rm -f + -$(TESTPYTHON) $(TESTPROG) $(QUICKTESTOPTS) + $(TESTPYTHON) $(TESTPROG) $(QUICKTESTOPTS) + +MEMTESTOPTS= $(QUICKTESTOPTS) -x test_dl test___all__ test_fork1 \ + test_longexp +memtest: all platform + -rm -f $(srcdir)/Lib/test/*.py[co] + -$(TESTPYTHON) $(TESTPROG) $(MEMTESTOPTS) + $(TESTPYTHON) $(TESTPROG) $(MEMTESTOPTS) + +# Install everything +install: altinstall bininstall maninstall + +# Install almost everything without disturbing previous versions +altinstall: altbininstall libinstall inclinstall libainstall \ + sharedinstall oldsharedinstall + +# Install shared libraries enabled by Setup +DESTDIRS= $(exec_prefix) $(LIBDIR) $(BINLIBDEST) $(DESTSHARED) + +oldsharedinstall: $(DESTSHARED) $(SHAREDMODS) + @for i in X $(SHAREDMODS); do \ + if test $$i != X; then \ + echo $(INSTALL_SHARED) $$i $(DESTSHARED)/`basename $$i`; \ + $(INSTALL_SHARED) $$i $(DESTDIR)$(DESTSHARED)/`basename $$i`; \ + fi; \ + done + +$(DESTSHARED): + @for i in $(DESTDIRS); \ + do \ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $$i"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ + else true; \ + fi; \ + done + + +# Install the interpreter (by creating a hard link to python$(VERSION)) +bininstall: altbininstall + -if test -f $(DESTDIR)$(BINDIR)/$(PYTHON) -o -h $(DESTDIR)$(BINDIR)/$(PYTHON); \ + then rm -f $(DESTDIR)$(BINDIR)/$(PYTHON); \ + else true; \ + fi + (cd $(DESTDIR)$(BINDIR); $(LN) python$(VERSION)$(EXE) $(PYTHON)) + (cd $(DESTDIR)$(BINDIR); $(LN) -sf python$(VERSION)-config python-config) + +# Install the interpreter with $(VERSION) affixed +# This goes into $(exec_prefix) +altbininstall: $(BUILDPYTHON) + @for i in $(BINDIR) $(LIBDIR); \ + do \ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $$i"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ + else true; \ + fi; \ + done + $(INSTALL_PROGRAM) $(BUILDPYTHON) $(DESTDIR)$(BINDIR)/python$(VERSION)$(EXE) + if test -f libpython$(VERSION)$(SO); then \ + if test "$(SO)" = .dll; then \ + $(INSTALL_SHARED) libpython$(VERSION)$(SO) $(DESTDIR)$(BINDIR); \ + else \ + $(INSTALL_SHARED) libpython$(VERSION)$(SO) $(DESTDIR)$(LIBDIR)/$(INSTSONAME); \ + if test libpython$(VERSION)$(SO) != $(INSTSONAME); then \ + (cd $(DESTDIR)$(LIBDIR); $(LN) -sf $(INSTSONAME) libpython$(VERSION)$(SO)); \ + fi \ + fi; \ + else true; \ + fi + +# Install the manual page +maninstall: + @for i in $(MANDIR) $(MANDIR)/man1; \ + do \ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $$i"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ + else true; \ + fi; \ + done + $(INSTALL_DATA) $(srcdir)/Misc/python.man \ + $(DESTDIR)$(MANDIR)/man1/python.1 + +# Install the library +PLATDIR= plat-$(MACHDEP) +EXTRAPLATDIR= +EXTRAMACHDEPPATH= +MACHDEPS= $(PLATDIR) $(EXTRAPLATDIR) +XMLLIBSUBDIRS= xml xml/dom xml/etree xml/parsers xml/sax +PLATMACDIRS= plat-mac plat-mac/Carbon plat-mac/lib-scriptpackages \ + plat-mac/lib-scriptpackages/_builtinSuites \ + plat-mac/lib-scriptpackages/CodeWarrior \ + plat-mac/lib-scriptpackages/Explorer \ + plat-mac/lib-scriptpackages/Finder \ + plat-mac/lib-scriptpackages/Netscape \ + plat-mac/lib-scriptpackages/StdSuites \ + plat-mac/lib-scriptpackages/SystemEvents \ + plat-mac/lib-scriptpackages/Terminal +PLATMACPATH=:plat-mac:plat-mac/lib-scriptpackages +LIBSUBDIRS= lib-tk site-packages test test/output test/data \ + test/decimaltestdata \ + encodings compiler hotshot \ + email email/mime email/test email/test/data \ + sqlite3 sqlite3/test \ + logging bsddb bsddb/test csv wsgiref \ + ctypes ctypes/test ctypes/macholib idlelib idlelib/Icons \ + distutils distutils/command distutils/tests $(XMLLIBSUBDIRS) \ + setuptools setuptools/command setuptools/tests setuptools.egg-info \ + curses $(MACHDEPS) +libinstall: $(BUILDPYTHON) $(srcdir)/Lib/$(PLATDIR) + @for i in $(SCRIPTDIR) $(LIBDEST); \ + do \ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $$i"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ + else true; \ + fi; \ + done + @for d in $(LIBSUBDIRS); \ + do \ + a=$(srcdir)/Lib/$$d; \ + if test ! -d $$a; then continue; else true; fi; \ + b=$(LIBDEST)/$$d; \ + if test ! -d $(DESTDIR)$$b; then \ + echo "Creating directory $$b"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$b; \ + else true; \ + fi; \ + done + @for i in $(srcdir)/Lib/*.py $(srcdir)/Lib/*.doc $(srcdir)/Lib/*.egg-info ; \ + do \ + if test -x $$i; then \ + $(INSTALL_SCRIPT) $$i $(DESTDIR)$(LIBDEST); \ + echo $(INSTALL_SCRIPT) $$i $(LIBDEST); \ + else \ + $(INSTALL_DATA) $$i $(DESTDIR)$(LIBDEST); \ + echo $(INSTALL_DATA) $$i $(LIBDEST); \ + fi; \ + done + @for d in $(LIBSUBDIRS); \ + do \ + a=$(srcdir)/Lib/$$d; \ + if test ! -d $$a; then continue; else true; fi; \ + if test `ls $$a | wc -l` -lt 1; then continue; fi; \ + b=$(LIBDEST)/$$d; \ + for i in $$a/*; \ + do \ + case $$i in \ + *CVS) ;; \ + *.py[co]) ;; \ + *.orig) ;; \ + *~) ;; \ + *) \ + if test -d $$i; then continue; fi; \ + if test -x $$i; then \ + echo $(INSTALL_SCRIPT) $$i $$b; \ + $(INSTALL_SCRIPT) $$i $(DESTDIR)$$b; \ + else \ + echo $(INSTALL_DATA) $$i $$b; \ + $(INSTALL_DATA) $$i $(DESTDIR)$$b; \ + fi;; \ + esac; \ + done; \ + done + $(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt + PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ + ./$(BUILDPYTHON) -Wi -tt $(DESTDIR)$(LIBDEST)/compileall.py \ + -d $(LIBDEST) -f \ + -x 'bad_coding|badsyntax|site-packages' $(DESTDIR)$(LIBDEST) + PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ + ./$(BUILDPYTHON) -Wi -tt -O $(DESTDIR)$(LIBDEST)/compileall.py \ + -d $(LIBDEST) -f \ + -x 'bad_coding|badsyntax|site-packages' $(DESTDIR)$(LIBDEST) + -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ + ./$(BUILDPYTHON) -Wi -t $(DESTDIR)$(LIBDEST)/compileall.py \ + -d $(LIBDEST)/site-packages -f \ + -x badsyntax $(DESTDIR)$(LIBDEST)/site-packages + -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ + ./$(BUILDPYTHON) -Wi -t -O $(DESTDIR)$(LIBDEST)/compileall.py \ + -d $(LIBDEST)/site-packages -f \ + -x badsyntax $(DESTDIR)$(LIBDEST)/site-packages + +# Create the PLATDIR source directory, if one wasn't distributed.. +$(srcdir)/Lib/$(PLATDIR): + mkdir $(srcdir)/Lib/$(PLATDIR) + cp $(srcdir)/Lib/plat-generic/regen $(srcdir)/Lib/$(PLATDIR)/regen + export PATH; PATH="`pwd`:$$PATH"; \ + export PYTHONPATH; PYTHONPATH="`pwd`/Lib"; \ + export DYLD_FRAMEWORK_PATH; DYLD_FRAMEWORK_PATH="`pwd`"; \ + export EXE; EXE="$(BUILDEXE)"; \ + cd $(srcdir)/Lib/$(PLATDIR); ./regen + +# Install the include files +INCLDIRSTOMAKE=$(INCLUDEDIR) $(CONFINCLUDEDIR) $(INCLUDEPY) $(CONFINCLUDEPY) +inclinstall: + @for i in $(INCLDIRSTOMAKE); \ + do \ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $$i"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ + else true; \ + fi; \ + done + @for i in $(srcdir)/Include/*.h; \ + do \ + echo $(INSTALL_DATA) $$i $(INCLUDEPY); \ + $(INSTALL_DATA) $$i $(DESTDIR)$(INCLUDEPY); \ + done + $(INSTALL_DATA) pyconfig.h $(DESTDIR)$(CONFINCLUDEPY)/pyconfig.h + +# Install the library and miscellaneous stuff needed for extending/embedding +# This goes into $(exec_prefix) +LIBPL= $(LIBP)/config +libainstall: all + @for i in $(LIBDIR) $(LIBP) $(LIBPL); \ + do \ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $$i"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ + else true; \ + fi; \ + done + @if test -d $(LIBRARY); then :; else \ + if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \ + if test "$(SO)" = .dll; then \ + $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBPL) ; \ + else \ + $(INSTALL_DATA) $(LIBRARY) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \ + $(RANLIB) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \ + fi; \ + else \ + echo Skip install of $(LIBRARY) - use make frameworkinstall; \ + fi; \ + fi + $(INSTALL_DATA) Modules/config.c $(DESTDIR)$(LIBPL)/config.c + $(INSTALL_DATA) Modules/python.o $(DESTDIR)$(LIBPL)/python.o + $(INSTALL_DATA) $(srcdir)/Modules/config.c.in $(DESTDIR)$(LIBPL)/config.c.in + $(INSTALL_DATA) Makefile $(DESTDIR)$(LIBPL)/Makefile + $(INSTALL_DATA) Modules/Setup $(DESTDIR)$(LIBPL)/Setup + $(INSTALL_DATA) Modules/Setup.local $(DESTDIR)$(LIBPL)/Setup.local + $(INSTALL_DATA) Modules/Setup.config $(DESTDIR)$(LIBPL)/Setup.config + $(INSTALL_SCRIPT) $(srcdir)/Modules/makesetup $(DESTDIR)$(LIBPL)/makesetup + $(INSTALL_SCRIPT) $(srcdir)/install-sh $(DESTDIR)$(LIBPL)/install-sh + # Substitution happens here, as the completely-expanded BINDIR + # is not available in configure + sed -e "s,@EXENAME@,$(BINDIR)/python$(VERSION)$(EXE)," < $(srcdir)/Misc/python-config.in >python-config + $(INSTALL_SCRIPT) python-config $(DESTDIR)$(BINDIR)/python$(VERSION)-config + rm python-config + @if [ -s Modules/python.exp -a \ + "`echo $(MACHDEP) | sed 's/^\(...\).*/\1/'`" = "aix" ]; then \ + echo; echo "Installing support files for building shared extension modules on AIX:"; \ + $(INSTALL_DATA) Modules/python.exp \ + $(DESTDIR)$(LIBPL)/python.exp; \ + echo; echo "$(LIBPL)/python.exp"; \ + $(INSTALL_SCRIPT) $(srcdir)/Modules/makexp_aix \ + $(DESTDIR)$(LIBPL)/makexp_aix; \ + echo "$(LIBPL)/makexp_aix"; \ + $(INSTALL_SCRIPT) $(srcdir)/Modules/ld_so_aix \ + $(DESTDIR)$(LIBPL)/ld_so_aix; \ + echo "$(LIBPL)/ld_so_aix"; \ + echo; echo "See Misc/AIX-NOTES for details."; \ + else true; \ + fi + @case "$(MACHDEP)" in beos*) \ + echo; echo "Installing support files for building shared extension modules on BeOS:"; \ + $(INSTALL_DATA) Misc/BeOS-NOTES $(DESTDIR)$(LIBPL)/README; \ + echo; echo "$(LIBPL)/README"; \ + $(INSTALL_SCRIPT) Modules/ar_beos $(DESTDIR)$(LIBPL)/ar_beos; \ + echo "$(LIBPL)/ar_beos"; \ + $(INSTALL_SCRIPT) Modules/ld_so_beos $(DESTDIR)$(LIBPL)/ld_so_beos; \ + echo "$(LIBPL)/ld_so_beos"; \ + echo; echo "See Misc/BeOS-NOTES for details."; \ + ;; \ + esac + +# Install the dynamically loadable modules +# This goes into $(exec_prefix) +sharedinstall: + $(RUNSHARED) ./$(BUILDPYTHON) -E $(srcdir)/setup.py install \ + --prefix=$(prefix) \ + --install-scripts=$(BINDIR) \ + --install-platlib=$(DESTSHARED) \ + --root=/$(DESTDIR) + +# Here are a couple of targets for MacOSX again, to install a full +# framework-based Python. frameworkinstall installs everything, the +# subtargets install specific parts. Much of the actual work is offloaded to +# the Makefile in Mac +# +# +# This target is here for backward compatiblity, previous versions of Python +# hadn't integrated framework installation in the normal install process. +frameworkinstall: install + +# On install, we re-make the framework +# structure in the install location, /Library/Frameworks/ or the argument to +# --enable-framework. If --enable-framework has been specified then we have +# automatically set prefix to the location deep down in the framework, so we +# only have to cater for the structural bits of the framework. + +frameworkinstallframework: frameworkinstallstructure install frameworkinstallmaclib + +frameworkinstallstructure: $(LDLIBRARY) + @if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \ + echo Not configured with --enable-framework; \ + exit 1; \ + else true; \ + fi + @for i in $(prefix)/Resources/English.lproj $(prefix)/lib; do\ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $(DESTDIR)$$i"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ + else true; \ + fi; \ + done + $(LN) -fsn include/python$(VERSION) $(DESTDIR)$(prefix)/Headers + $(INSTALL_DATA) $(RESSRCDIR)/Info.plist $(DESTDIR)$(prefix)/Resources/Info.plist + $(INSTALL_DATA) $(RESSRCDIR)/version.plist $(DESTDIR)$(prefix)/Resources/version.plist + $(INSTALL_DATA) $(RESSRCDIR)/English.lproj/InfoPlist.strings \ + $(DESTDIR)$(prefix)/Resources/English.lproj/InfoPlist.strings + $(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current + $(LN) -fsn Versions/Current/Python $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Python + $(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers + $(LN) -fsn Versions/Current/Resources $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Resources + $(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY) + +# This installs Mac/Lib into the framework +# Install a number of symlinks to keep software that expects a normal unix +# install (which includes python-config) happy. +frameworkinstallmaclib: + ln -fs "../../../Python" "$(DESTDIR)$(prefix)/lib/python$(VERSION)/config/libpython$(VERSION).a" + cd Mac && $(MAKE) installmacsubtree DESTDIR="$(DESTDIR)" + +# This installs the IDE, the Launcher and other apps into /Applications +frameworkinstallapps: + cd Mac && $(MAKE) installapps DESTDIR="$(DESTDIR)" + +# This install the unix python and pythonw tools in /usr/local/bin +frameworkinstallunixtools: + cd Mac && $(MAKE) installunixtools DESTDIR="$(DESTDIR)" + +frameworkaltinstallunixtools: + cd Mac && $(MAKE) altinstallunixtools DESTDIR="$(DESTDIR)" + +# This installs the Demos and Tools into the applications directory. +# It is not part of a normal frameworkinstall +frameworkinstallextras: + cd Mac && Make installextras DESTDIR="$(DESTDIR)" + +# This installs a few of the useful scripts in Tools/scripts +scriptsinstall: + SRCDIR=$(srcdir) $(RUNSHARED) \ + ./$(BUILDPYTHON) $(srcdir)/Tools/scripts/setup.py install \ + --prefix=$(prefix) \ + --install-scripts=$(BINDIR) \ + --root=/$(DESTDIR) + +# Build the toplevel Makefile +Makefile.pre: Makefile.pre.in config.status + CONFIG_FILES=Makefile.pre CONFIG_HEADERS= $(SHELL) config.status + $(MAKE) -f Makefile.pre Makefile + +# Run the configure script. +config.status: $(srcdir)/configure + $(SHELL) $(srcdir)/configure $(CONFIG_ARGS) + +.PRECIOUS: config.status $(BUILDPYTHON) Makefile Makefile.pre + +# Some make's put the object file in the current directory +.c.o: + $(CC) -c $(PY_CFLAGS) -o $@ $< + +# Run reindent on the library +reindent: + ./python$(EXEEXT) $(srcdir)/Tools/scripts/reindent.py -r $(srcdir)/Lib + +# Rerun configure with the same options as it was run last time, +# provided the config.status script exists +recheck: + $(SHELL) config.status --recheck + $(SHELL) config.status + +# Rebuild the configure script from configure.in; also rebuild pyconfig.h.in +autoconf: + (cd $(srcdir); autoconf) + (cd $(srcdir); autoheader) + +# Create a tags file for vi +tags:: + cd $(srcdir); \ + ctags -w -t Include/*.h; \ + for i in $(SRCDIRS); do ctags -w -t -a $$i/*.[ch]; \ + done; \ + sort -o tags tags + +# Create a tags file for GNU Emacs +TAGS:: + cd $(srcdir); \ + etags Include/*.h; \ + for i in $(SRCDIRS); do etags -a $$i/*.[ch]; done + +# Sanitation targets -- clean leaves libraries, executables and tags +# files, which clobber removes those as well +pycremoval: + find $(srcdir) -name '*.py[co]' -exec rm -f {} ';' + +clean: pycremoval + find . -name '*.o' -exec rm -f {} ';' + find . -name '*.s[ol]' -exec rm -f {} ';' + find $(srcdir)/build -name 'fficonfig.h' -exec rm -f {} ';' || true + find $(srcdir)/build -name 'fficonfig.py' -exec rm -f {} ';' || true + +clobber: clean + -rm -f $(BUILDPYTHON) $(PGEN) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \ + tags TAGS \ + config.cache config.log pyconfig.h Modules/config.c + -rm -rf build platform + -rm -rf $(PYTHONFRAMEWORKDIR) + +# Make things extra clean, before making a distribution: +# remove all generated files, even Makefile[.pre] +# Keep configure and Python-ast.[ch], it's possible they can't be generated +distclean: clobber + -rm -f core Makefile Makefile.pre config.status \ + Modules/Setup Modules/Setup.local Modules/Setup.config + find $(srcdir) '(' -name '*.fdc' -o -name '*~' \ + -o -name '[@,#]*' -o -name '*.old' \ + -o -name '*.orig' -o -name '*.rej' \ + -o -name '*.bak' ')' \ + -exec rm -f {} ';' + +# Check for smelly exported symbols (not starting with Py/_Py) +smelly: all + nm -p $(LIBRARY) | \ + sed -n "/ [TDB] /s/.* //p" | grep -v "^_*Py" | sort -u; \ + +# Find files with funny names +funny: + find $(DISTDIRS) -type d \ + -o -name '*.[chs]' \ + -o -name '*.py' \ + -o -name '*.doc' \ + -o -name '*.sty' \ + -o -name '*.bib' \ + -o -name '*.dat' \ + -o -name '*.el' \ + -o -name '*.fd' \ + -o -name '*.in' \ + -o -name '*.tex' \ + -o -name '*,[vpt]' \ + -o -name 'Setup' \ + -o -name 'Setup.*' \ + -o -name README \ + -o -name Makefile \ + -o -name ChangeLog \ + -o -name Repository \ + -o -name Root \ + -o -name Entries \ + -o -name Tag \ + -o -name tags \ + -o -name TAGS \ + -o -name .cvsignore \ + -o -name MANIFEST \ + -o -print + +# Dependencies + +Python/thread.o: $(srcdir)/Python/thread_atheos.h $(srcdir)/Python/thread_beos.h $(srcdir)/Python/thread_cthread.h $(srcdir)/Python/thread_foobar.h $(srcdir)/Python/thread_lwp.h $(srcdir)/Python/thread_nt.h $(srcdir)/Python/thread_os2.h $(srcdir)/Python/thread_pth.h $(srcdir)/Python/thread_pthread.h $(srcdir)/Python/thread_sgi.h $(srcdir)/Python/thread_solaris.h $(srcdir)/Python/thread_wince.h + +# Declare targets that aren't real files +.PHONY: all sharedmods oldsharedmods test quicktest memtest +.PHONY: install altinstall oldsharedinstall bininstall altbininstall +.PHONY: maninstall libinstall inclinstall libainstall sharedinstall +.PHONY: frameworkinstall frameworkinstallframework frameworkinstallstructure +.PHONY: frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools +.PHONY: frameworkaltinstallunixtools recheck autoconf clean clobber distclean +.PHONY: smelly funny + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY + +# Rules appended by makedepend + +Modules/threadmodule.o: $(srcdir)/Modules/threadmodule.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/threadmodule.c -o Modules/threadmodule.o +Modules/threadmodule$(SO): Modules/threadmodule.o; $(LDSHARED) Modules/threadmodule.o -o Modules/threadmodule$(SO) +Modules/signalmodule.o: $(srcdir)/Modules/signalmodule.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/signalmodule.c -o Modules/signalmodule.o +Modules/signalmodule$(SO): Modules/signalmodule.o; $(LDSHARED) Modules/signalmodule.o -o Modules/signalmodule$(SO) +Modules/posixmodule.o: $(srcdir)/Modules/posixmodule.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/posixmodule.c -o Modules/posixmodule.o +Modules/posixmodule$(SO): Modules/posixmodule.o; $(LDSHARED) Modules/posixmodule.o -o Modules/posixmodule$(SO) +Modules/errnomodule.o: $(srcdir)/Modules/errnomodule.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/errnomodule.c -o Modules/errnomodule.o +Modules/errnomodule$(SO): Modules/errnomodule.o; $(LDSHARED) Modules/errnomodule.o -o Modules/errnomodule$(SO) +Modules/pwdmodule.o: $(srcdir)/Modules/pwdmodule.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/pwdmodule.c -o Modules/pwdmodule.o +Modules/pwdmodule$(SO): Modules/pwdmodule.o; $(LDSHARED) Modules/pwdmodule.o -o Modules/pwdmodule$(SO) +Modules/_sre.o: $(srcdir)/Modules/_sre.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/_sre.c -o Modules/_sre.o +Modules/_sre$(SO): Modules/_sre.o; $(LDSHARED) Modules/_sre.o -o Modules/_sre$(SO) +Modules/_codecsmodule.o: $(srcdir)/Modules/_codecsmodule.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/_codecsmodule.c -o Modules/_codecsmodule.o +Modules/_codecsmodule$(SO): Modules/_codecsmodule.o; $(LDSHARED) Modules/_codecsmodule.o -o Modules/_codecsmodule$(SO) +Modules/zipimport.o: $(srcdir)/Modules/zipimport.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/zipimport.c -o Modules/zipimport.o +Modules/zipimport$(SO): Modules/zipimport.o; $(LDSHARED) Modules/zipimport.o -o Modules/zipimport$(SO) +Modules/symtablemodule.o: $(srcdir)/Modules/symtablemodule.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/symtablemodule.c -o Modules/symtablemodule.o +Modules/_symtablemodule$(SO): Modules/symtablemodule.o; $(LDSHARED) Modules/symtablemodule.o -o Modules/_symtablemodule$(SO) +Modules/xxsubtype.o: $(srcdir)/Modules/xxsubtype.c; $(CC) $(PY_CFLAGS) -c $(srcdir)/Modules/xxsubtype.c -o Modules/xxsubtype.o +Modules/xxsubtype$(SO): Modules/xxsubtype.o; $(LDSHARED) Modules/xxsubtype.o -o Modules/xxsubtype$(SO) diff --git a/tests/examplefiles/Object.st b/tests/examplefiles/Object.st new file mode 100644 index 0000000..4a1ca4c --- /dev/null +++ b/tests/examplefiles/Object.st @@ -0,0 +1,4394 @@ +!ProtoObject subclass: #Object + instanceVariableNames: '' + classVariableNames: 'DependentsFields' + poolDictionaries: '' + category: 'Kernel-Objects'! + +!Object methodsFor: '*39Deprecated' stamp: 'gk 2/24/2004 08:49'! +beep + "Deprecated." + + self deprecated: 'Use Beeper class>>beep instead.'. + Beeper beep! ! + +!Object methodsFor: '*39Deprecated' stamp: 'gk 2/24/2004 08:50'! +beepPrimitive + "Deprecated. Beep in the absence of sound support." + + self deprecated: 'Use Beeper class>>beep or Beeper class>>beepPrimitive instead.'. + Beeper beepPrimitive! ! + +!Object methodsFor: '*39Deprecated' stamp: 'md 12/12/2003 17:02'! +beep: soundName + "Make the given sound, unless the making of sound is disabled in Preferences." + + self deprecated: 'Use SampledSound>>playSoundNamed: instead.'. + Preferences soundsEnabled + ifTrue: [self playSoundNamed: soundName] +! ! + +!Object methodsFor: '*39Deprecated' stamp: 'sd 11/19/2004 16:57'! +contentsGetz: x + self deprecated: 'there is no method named contents in object and in addition only one sender in a method not called'. + self contents: x! ! + +!Object methodsFor: '*39Deprecated' stamp: 'sd 11/13/2003 21:10'! +deprecatedExplanation: aString + "This method is OBSOLETE. Use #deprecated: instead." + self deprecated: 'Use Object>>deprecated: instead of deprecatedExplanation:.'. + + Preferences showDeprecationWarnings ifTrue: + [Deprecation signal: ('{1} has been deprecated. {2}' translated format: {thisContext sender printString. aString})]! ! + +!Object methodsFor: '*39Deprecated' stamp: 'sd 11/13/2003 21:11'! +deprecated: aBlock explanation: aString + "This method is OBSOLETE. Use #deprecated:block: instead." + self deprecated: 'Use Object>>deprecated:block: instead of deprecated:explanation:.'. + + Preferences showDeprecationWarnings ifTrue: + [Deprecation + signal: ('{1} has been deprecated. {2}' translated format: {thisContext sender printString. aString})]. + ^ aBlock value. +! ! + +!Object methodsFor: '*39Deprecated' stamp: 'md 12/12/2003 16:25'! +doIfNotNil: aBlock + self deprecated: 'use ifNotNilDo:'. + ^ self ifNotNilDo: aBlock +! ! + +!Object methodsFor: '*39Deprecated' stamp: 'md 11/27/2004 12:20'! +ifKindOf: aClass thenDo: aBlock + self deprecated: 'Deprecated. Just use #isKindOf:'. + ^ (self isKindOf: aClass) ifTrue: [aBlock value: self]! ! + +!Object methodsFor: '*39Deprecated' stamp: 'gk 2/23/2004 20:51'! +playSoundNamed: soundName + "Deprecated. + Play the sound with the given name." + + self deprecated: 'Use "SoundService default playSoundNamed: aName" instead.'. + SoundService default playSoundNamed: soundName! ! + + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:23'! +aidaCanBeLocked + "can we get an exclusive lock on that object (not already locked)?" + ^false! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:23'! +aidaDontCache + "don't cache web content in a browser. Appropriate header is added to http response" + ^false! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:24'! +aidaIsLocked + "is object locked exclusively?" + ^false! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:24'! +aidaLock + "get an exclusive lock on that object. Until unlocked, noon else can get that lock. Return false if already locked, true if successfull" + ^false! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:24'! +aidaUnlock + "release an exclusive lock if any" + ^true! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:24'! +app + "fastest and most convinient way to find a web app for that object" + ^self webAppFor: self firstSessionFromStack! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:25'! +contentType + + "Janko Mivsek, apr98" + "return 'text/html' as content type for web pages" + + ^'text/html'! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:25'! +deepSearchOfClass: aClassName + "finf all objects of that class down in object hierarchy" + | objectDictionary class | + objectDictionary := IdentityDictionary new. + self deepCopyNotIn: objectDictionary. + class := aClassName asSymbol. + ^objectDictionary keys select: [:each | each class name = class].! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:26'! +deepSearchOfObsoleteClasses + "find all objects of obsolete classes down in object hierarchy" + | objectDictionary | + objectDictionary := IdentityDictionary new. + self deepCopyNotIn: objectDictionary. + ^objectDictionary keys select: [:each | each class isObsolete].! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:26'! +expiresTimestamp + "until when content of this object wont be changed" + "used in http response, override if you like to be included" + ^self modifiedTimestamp "to reload pages immediately"! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:26'! +firstAppFromStack + "try to find a first sender up in calling stack, who is WebApplication" + | context | + context := thisContext. + [context notNil] whileTrue: [ + (context receiver isKindOf: WebApplication) ifTrue: [^context receiver]. + context := context sender]. + ^self firstSessionFromStack lastApp! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/25/2007 21:34'! +firstSessionFromStack + "try to find a first sender up in calling stack, who is AIDASite and get session if that call" + | context | + context := thisContext. + [context notNil] whileTrue: [ + (context receiver isKindOf: AIDASite) ifTrue: [^(context at: 3) "always?"]. + context := context sender]. + ^nil! ! + +!Object methodsFor: '*Aida' stamp: 'mivsek 1/10/2008 18:14'! +forLanguage: aLanguageCodeSymbol + "for multilingual support: returns an apropriate instance of itself for that language. + Langage is defined by ISO 639 2-letter language code, see + http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" + ^false! ! + +!Object methodsFor: '*Aida' stamp: 'mivsek 1/10/2008 18:14'! +isMultilingual + "for multilingual support: override this if your domain object responds + to #forLanguage: and returns an apropriate instance of itself for that language" + ^false! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:27'! +isVersionedObject + ^false! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:27'! +isWebApplication + ^false! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:27'! +isWebStyle + ^false! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:27'! +modifiedTimestamp + "when this object was last modified" + "used in http response, override if you like to be included" + ^nil! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:28'! +preferedUrl + "override with a suggestion for url of this method!! If not already used, + it will be considered by URLResolver during automatic url generation" + ^nil! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:28'! +printWebAppNotFoundFor: aSession + | page | + page := WebPage new. + page addText: 'Cannot find aWebApplication for object a', self class name. + ^page! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:28'! +printWebPageFor: aSession + "find appropriate web application to represent self as web page" + + | webApp | + webApp := self webAppFor: aSession. + ^webApp notNil + ifTrue: [webApp printWebPage] + ifFalse: [self printWebAppNotFoundFor: aSession]! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:29'! +sendOver: aStream + "from Wiki rendering" + self printOn: aStream! ! + +!Object methodsFor: '*Aida' stamp: 'JM 4/22/2007 15:29'! +webAppFor: aSession + | webApp | + aSession isNil ifTrue: [^nil]. + webApp := aSession webAppFor: self. + webApp notNil ifTrue: [^webApp]. + webApp := WebApplication newFor: self on: aSession. + webApp notNil ifTrue: [aSession addWebApp: webApp for: self]. + ^webApp! ! + + +!Object methodsFor: '*DynamicBindings' stamp: 'svp 3/6/2003 16:08'! +binding + "Answer the DynamicBinding for the receiver (if any)" + + ^Bindings bindingFor: self ifNotBound: [nil]! ! + +!Object methodsFor: '*DynamicBindings' stamp: 'svp 4/29/2003 00:35'! +binding: anObject + "Set the dynamic binding for the receiver, if anObject is nil, then + remove the receiver's dynamic binding (if any)" + + ^anObject + ifNil: [self removeBinding] + ifNotNil: [Bindings bind: self to: anObject]! ! + +!Object methodsFor: '*DynamicBindings' stamp: 'svp 3/6/2003 16:09'! +hasBinding + "Answer whether or not the receiver has a dynamic binding" + + ^Bindings includesKey: self! ! + +!Object methodsFor: '*DynamicBindings' stamp: 'svp 3/6/2003 16:09'! +removeBinding + "Remove the dynamic binding associated with the receiver" + + ^Bindings removeKey: self ifAbsent: []! ! + + +!Object methodsFor: '*KomHttpServer' stamp: 'SVP 8/23/1999 19:17'! +asHtmlDocumentForRequest: aNetworkRequest + + self error: + ('The requested object (', + self asString, + '), could not be converted into HTML for your browser.')! ! + +!Object methodsFor: '*KomHttpServer' stamp: 'SVP 8/23/1999 19:23'! +asHttpResponseTo: anHttpRequest + + ^(self asHtmlDocumentForRequest: anHttpRequest) + asHttpResponseTo: anHttpRequest +! ! + +!Object methodsFor: '*KomHttpServer' stamp: 'svp 5/16/2003 12:47'! +isComancheModule + + ^false! ! + +!Object methodsFor: '*KomHttpServer' stamp: 'SVP 8/17/1999 17:51'! +mimeType + + ^MIMEDocument defaultContentType! ! + + +!Object methodsFor: '*Morphic-NewCurve-testing''' stamp: 'wiz 12/31/2005 21:31'! +isNonZero +"Overriden in Number. This returns the backstop answer for non-numbers" +^false.! ! + + +!Object methodsFor: '*Pinesoft-EventEnhancements' stamp: 'gvc 10/25/2006 18:18'! +when: anEventSelector +send: aMessageSelector +to: anObject +exclusive: aValueHolder + + self + when: anEventSelector + evaluate: ((ExclusiveWeakMessageSend + receiver: anObject + selector: aMessageSelector) + basicExecuting: aValueHolder)! ! + +!Object methodsFor: '*Pinesoft-EventEnhancements' stamp: 'gvc 10/25/2006 18:17'! +when: anEventSelector +send: aMessageSelector +to: anObject +with: anArg +exclusive: aValueHolder + + self + when: anEventSelector + evaluate: ((ExclusiveWeakMessageSend + receiver: anObject + selector: aMessageSelector + arguments: (Array with: anArg)) + basicExecuting: aValueHolder)! ! + +!Object methodsFor: '*Pinesoft-EventEnhancements' stamp: 'gvc 10/25/2006 18:17'! +when: anEventSelector +send: aMessageSelector +to: anObject +withArguments: anArgArray +exclusive: aValueHolder + + self + when: anEventSelector + evaluate: ((ExclusiveWeakMessageSend + receiver: anObject + selector: aMessageSelector + arguments: anArgArray) + basicExecuting: aValueHolder)! ! + +!Object methodsFor: '*Pinesoft-EventEnhancements' stamp: 'gvc 10/24/2006 11:50'! +when: anEventSelector +sendOnce: aMessageSelector +to: anObject + + self + when: anEventSelector + evaluate: (NonReentrantWeakMessageSend + receiver: anObject + selector: aMessageSelector)! ! + +!Object methodsFor: '*Pinesoft-EventEnhancements' stamp: 'gvc 10/24/2006 11:49'! +when: anEventSelector +sendOnce: aMessageSelector +to: anObject +with: anArg + + self + when: anEventSelector + evaluate: (NonReentrantWeakMessageSend + receiver: anObject + selector: aMessageSelector + arguments: (Array with: anArg))! ! + +!Object methodsFor: '*Pinesoft-EventEnhancements' stamp: 'gvc 10/24/2006 11:49'! +when: anEventSelector +sendOnce: aMessageSelector +to: anObject +withArguments: anArgArray + + self + when: anEventSelector + evaluate: (NonReentrantWeakMessageSend + receiver: anObject + selector: aMessageSelector + arguments: anArgArray)! ! + + +!Object methodsFor: '*Pinesoft-Widgets' stamp: 'gvc 1/10/2007 11:41'! +okToClose + "Sent to models when a window closing. + Allows this check to be independent of okToChange." + + ^true! ! + +!Object methodsFor: '*Pinesoft-Widgets' stamp: 'gvc 4/17/2007 17:41'! +taskbarIcon + "Answer the icon for the receiver in a task bar + or nil for the default." + + ^self class taskbarIcon! ! + + +!Object methodsFor: '*Pinesoft-Widgets-override' stamp: 'gvc 9/4/2007 12:32'! +windowActiveOnFirstClick + "Return true if my window should be active on first click." + + ^true! ! + + +!Object methodsFor: '*SeasideAdaptersCompatibility' stamp: 'pmm 11/25/2007 14:17'! +toString + ^self! ! + + +!Object methodsFor: '*Tools-Explorer' stamp: 'stephaneducasse 9/17/2005 21:52'! +exploreAndYourself + "i.e. explore; yourself. Thisway i can peek w/o typing all the parentheses" + self explore. + ^self! ! + +!Object methodsFor: '*Tools-Explorer' stamp: 'stephaneducasse 9/17/2005 21:48'! +exploreWithLabel: label + + ^ ObjectExplorer new openExplorerFor: self withLabel: +label! ! + + +!Object methodsFor: '*kernel-extensions-flagging' stamp: 'mtf 1/26/2008 23:34'! +deprecated + "Warn that the sending method has been deprecated." + + Preferences showDeprecationWarnings ifTrue: + [Deprecation signal: thisContext sender printString, ' has been deprecated.']! ! + + +!Object methodsFor: '*kernel-extensions-flexibility' stamp: 'kph 1/27/2008 19:21'! +askFor: selector + + "returns true or false" + + ^ (self askFor: selector ifAbsent: nil) == true! ! + +!Object methodsFor: '*kernel-extensions-flexibility' stamp: 'kph 10/17/2007 14:01'! +askFor: selector ifAbsent: aBlock + + "enables a default value to be specified in order to be tolerant of potentially missing methods + + e.g. + (myPoint askFor: #originOffset) ifAbsent: [ 0@0 ]. + " + + ^ (self class canUnderstand: selector) ifFalse: [ aBlock value ] ifTrue: [self perform: selector]! ! + + +!Object methodsFor: '*kernel-extensions-logging' stamp: 'mtf 1/26/2008 23:52'! +log + "This method provides the univeral entry point fo all logging mechanisms" + + "Options: + 1. Null for null logging + 2. A LogRouter instance wih a FrameworkAdaptor. + 3. CurrentLog a process local variable supplying a LogRouter" + + ^ (Smalltalk at: #CurrentLog ifAbsent: [ Null default ]) value + sender: thisContext sender; beginEntry; yourself! ! + + +!Object methodsFor: '*magritte-model-accessing' stamp: 'lr 3/9/2006 11:31'! +description + "Return the description of the reciever. Subclasses might override this message to return instance-based descriptions." + + ^ self class description! ! + +!Object methodsFor: '*magritte-model-accessing' stamp: 'lr 3/9/2006 11:31'! +mementoClass + "Return a class to be used to remember or cache the receiver, namely a memento object." + + ^ MACheckedMemento! ! + + +!Object methodsFor: '*magritte-model-model' stamp: 'lr 3/9/2006 11:31'! +readUsing: aDescription + "Dispatch the read-access to the receiver using the accessor of aDescription." + + ^ aDescription accessor read: self! ! + +!Object methodsFor: '*magritte-model-model' stamp: 'lr 3/9/2006 11:31'! +write: anObject using: aDescription + "Dispatch the write-access to the receiver of anObject using the accessor of aDescription." + + aDescription accessor write: anObject to: self! ! + + +!Object methodsFor: '*magritte-model-testing' stamp: 'lr 3/9/2006 11:31'! +isDescription + ^ false! ! + + +!Object methodsFor: '*magritte-morph-converting' stamp: 'lr 3/9/2006 11:33'! +asMorph + ^ self description asMorphOn: self! ! + + +!Object methodsFor: '*magritte-seaside-converting' stamp: 'lr 3/9/2006 11:33'! +asComponent + ^ self description asComponentOn: self! ! + + +!Object methodsFor: '*monticello' stamp: 'dvf 8/10/2004 23:25'! +isConflict + ^false! ! + + +!Object methodsFor: '*null' stamp: 'kph 9/6/2007 23:31'! +ifNull: aBlock + + ^ self! ! + +!Object methodsFor: '*null' stamp: 'kph 9/6/2007 23:33'! +isNull + + ^ false! ! + +!Object methodsFor: '*null' stamp: 'kph 4/12/2007 08:27'! +orNull + + ^ self! ! + + +!Object methodsFor: '*ob-tools-inspector' stamp: 'lr 6/5/2008 11:07'! +basicInspectorNodes + + + | nodes | + nodes := OrderedCollection new: self class instSize + self basicSize + 5. + nodes add: self selfInspectorNode. + self class allInstVarNames withIndexDo: [ :name :index | + nodes add: (OTNamedVariableNode on: self index: index name: name) ]. + 1 to: self basicSize do: [ :index | + nodes add: (OTIndexedVariableNode on: self index: index) ]. + ^ nodes! ! + +!Object methodsFor: '*ob-tools-inspector' stamp: 'lr 6/5/2008 11:07'! +protocolInspectorNodes + + + ^ self class allSelectors asArray sort + collect: [ :each | OTProtocolInspectorNode on: self selector: each ]! ! + +!Object methodsFor: '*ob-tools-inspector' stamp: 'lr 6/5/2008 09:58'! +selfInspectorNode + ^ OTDerivedInspectorNode on: self label: 'self' block: [ :obj | obj ]! ! + + +!Object methodsFor: '*omnibrowser-converting' stamp: 'cwp 4/17/2006 12:16'! +asAnnouncement + ^ self! ! + + +!Object methodsFor: '*pier-model' stamp: 'lr 3/9/2006 11:29'! +accept: aVisitor + self subclassResponsibility! ! + +!Object methodsFor: '*pier-model' stamp: 'lr 3/9/2006 11:29'! +acceptDecorated: aVisitor + self accept: aVisitor! ! + + +!Object methodsFor: '*rio-kernel' stamp: 'kph 3/8/2007 21:25'! +isRio + + ^ false! ! + + +!Object methodsFor: '*scriptaculous' stamp: 'lr 1/4/2007 17:20'! +asFunction + ^ self asFunction: #()! ! + +!Object methodsFor: '*scriptaculous' stamp: 'lr 1/4/2007 17:21'! +asFunction: aCollection + ^ SUFunction new add: self; arguments: aCollection! ! + +!Object methodsFor: '*scriptaculous' stamp: 'lr 4/11/2006 19:49'! +asJavascript + ^ String streamContents: [ :stream | self javascriptOn: stream ]! ! + + +!Object methodsFor: '*scriptaculous-printing' stamp: 'lr 4/20/2006 21:10'! +javascriptOn: aStream + self printOn: aStream! ! + + +!Object methodsFor: '*seaside2' stamp: 'lr 6/5/2007 21:35'! +deprecatedApi + self deprecatedApi: thisContext sender displayString! ! + +!Object methodsFor: '*seaside2' stamp: 'lr 6/5/2007 21:35'! +deprecatedApi: aString + WADeprecatedApi raiseSignal: aString! ! + +!Object methodsFor: '*seaside2' stamp: 'lr 5/9/2007 08:47'! +inspectorFields + | members | + members := Array new writeStream. + self class allInstVarNames withIndexDo: [ :each :index | + members nextPut: each -> (self instVarAt: index) ]. + self class isVariable ifTrue: [ + 1 to: self size do: [ :index | + members nextPut: index -> (self at: index) ] ]. + ^ members contents! ! + +!Object methodsFor: '*seaside2' stamp: 'avi 3/14/2005 15:19'! +labelForSelector: aSymbol + ^ aSymbol asCapitalizedPhrase! ! + +!Object methodsFor: '*seaside2' stamp: 'pmm 4/7/2007 17:14'! +renderOn: aRenderer + "Override this method to customize how objects (not components) are rendered when passed as an argument to #render:. The default is the return value of #displayString. + Just remember that you can not use #callback:, #on:of:, or #call:" + + aRenderer text: self! ! + +!Object methodsFor: '*seaside2' stamp: 'lr 3/19/2007 23:13'! +restoreFromSnapshot: anObject + self copyFrom: anObject! ! + +!Object methodsFor: '*seaside2' stamp: 'avi 9/1/2004 21:20'! +snapshotCopy + ^ self shallowCopy! ! + +!Object methodsFor: '*seaside2' stamp: 'lr 10/28/2007 14:42'! +validationError: message + ^WAValidationNotification raiseSignal: message! ! + + +!Object methodsFor: '*seaside2-encoding' stamp: 'lr 3/26/2007 20:16'! +encodeOn: aDocument + aDocument print: self displayString! ! + + +!Object methodsFor: '*seaside2-squeak' stamp: 'pmm 5/22/2007 22:10'! +beMutable + "for VW compatibility, a hack that allows to cache a value in a literal array"! ! + +!Object methodsFor: '*seaside2-squeak' stamp: 'lr 7/12/2005 17:01'! +displayString + ^ self asString! ! + + +!Object methodsFor: '*services-base' stamp: 'rr 3/21/2006 11:54'! +requestor + "returns the focused window's requestor" + + "SystemWindow focusedWindow ifNotNilDo: [:w | ^ w requestor]." + + "triggers an infinite loop" + + ^ Requestor default! ! + + +!Object methodsFor: '*system-support' stamp: 'dvf 8/23/2003 12:27'! +systemNavigation + + ^ SystemNavigation default! ! + + +!Object methodsFor: '*tools-browser' stamp: 'mu 3/6/2004 15:13'! +browse + self systemNavigation browseClass: self class! ! + +!Object methodsFor: '*tools-browser' stamp: 'mu 3/11/2004 16:00'! +browseHierarchy + self systemNavigation browseHierarchy: self class! ! + + +!Object methodsFor: '*universes' stamp: 'ls 11/26/2006 12:33'! +isUPackage + ^false! ! + +!Object methodsFor: '*universes' stamp: 'ls 11/26/2006 12:33'! +isUPackageCategory + ^false! ! + + +!Object methodsFor: 'accessing' stamp: 'sw 4/30/1998 12:18'! +addInstanceVarNamed: aName withValue: aValue + "Add an instance variable named aName and give it value aValue" + self class addInstVarName: aName asString. + self instVarAt: self class instSize put: aValue! ! + +!Object methodsFor: 'accessing' stamp: 'yo 6/29/2004 11:39'! +at: index + "Primitive. Assumes receiver is indexable. Answer the value of an + indexable element in the receiver. Fail if the argument index is not an + Integer or is out of bounds. Essential. See Object documentation + whatIsAPrimitive." + + + index isInteger ifTrue: + [self class isVariable + ifTrue: [self errorSubscriptBounds: index] + ifFalse: [self errorNotIndexable]]. + index isNumber + ifTrue: [^self at: index asInteger] + ifFalse: [self errorNonIntegerIndex]! ! + +!Object methodsFor: 'accessing'! +at: index modify: aBlock + "Replace the element of the collection with itself transformed by the block" + ^ self at: index put: (aBlock value: (self at: index))! ! + +!Object methodsFor: 'accessing' stamp: 'yo 6/29/2004 13:08'! +at: index put: value + "Primitive. Assumes receiver is indexable. Store the argument value in + the indexable element of the receiver indicated by index. Fail if the + index is not an Integer or is out of bounds. Or fail if the value is not of + the right type for this kind of collection. Answer the value that was + stored. Essential. See Object documentation whatIsAPrimitive." + + + index isInteger ifTrue: + [self class isVariable + ifTrue: [(index >= 1 and: [index <= self size]) + ifTrue: [self errorImproperStore] + ifFalse: [self errorSubscriptBounds: index]] + ifFalse: [self errorNotIndexable]]. + index isNumber + ifTrue: [^self at: index asInteger put: value] + ifFalse: [self errorNonIntegerIndex]! ! + +!Object methodsFor: 'accessing' stamp: 'yo 9/20/2004 10:22'! +basicAddInstanceVarNamed: aName withValue: aValue + "Add an instance variable named aName and give it value aValue" + self class addInstVarName: aName asString. + self instVarAt: self class instSize put: aValue! ! + +!Object methodsFor: 'accessing'! +basicAt: index + "Primitive. Assumes receiver is indexable. Answer the value of an + indexable element in the receiver. Fail if the argument index is not an + Integer or is out of bounds. Essential. Do not override in a subclass. See + Object documentation whatIsAPrimitive." + + + index isInteger ifTrue: [self errorSubscriptBounds: index]. + index isNumber + ifTrue: [^self basicAt: index asInteger] + ifFalse: [self errorNonIntegerIndex]! ! + +!Object methodsFor: 'accessing'! +basicAt: index put: value + "Primitive. Assumes receiver is indexable. Store the second argument + value in the indexable element of the receiver indicated by index. Fail + if the index is not an Integer or is out of bounds. Or fail if the value is + not of the right type for this kind of collection. Answer the value that + was stored. Essential. Do not override in a subclass. See Object + documentation whatIsAPrimitive." + + + index isInteger + ifTrue: [(index >= 1 and: [index <= self size]) + ifTrue: [self errorImproperStore] + ifFalse: [self errorSubscriptBounds: index]]. + index isNumber + ifTrue: [^self basicAt: index asInteger put: value] + ifFalse: [self errorNonIntegerIndex]! ! + +!Object methodsFor: 'accessing'! +basicSize + "Primitive. Answer the number of indexable variables in the receiver. + This value is the same as the largest legal subscript. Essential. Do not + override in any subclass. See Object documentation whatIsAPrimitive." + + + "The number of indexable fields of fixed-length objects is 0" + ^0 ! ! + +!Object methodsFor: 'accessing'! +bindWithTemp: aBlock + ^ aBlock value: self value: nil! ! + +!Object methodsFor: 'accessing' stamp: 'md 10/7/2004 15:43'! +ifNil: nilBlock ifNotNilDo: aBlock + "Evaluate aBlock with the receiver as its argument." + + ^ aBlock value: self +! ! + +!Object methodsFor: 'accessing' stamp: 'di 11/8/2000 21:04'! +ifNotNilDo: aBlock + "Evaluate the given block with the receiver as its argument." + + ^ aBlock value: self +! ! + +!Object methodsFor: 'accessing' stamp: 'md 10/7/2004 15:43'! +ifNotNilDo: aBlock ifNil: nilBlock + "Evaluate aBlock with the receiver as its argument." + + ^ aBlock value: self +! ! + +!Object methodsFor: 'accessing' stamp: 'ajh 1/21/2003 12:59'! +in: aBlock + "Evaluate the given block with the receiver as its argument." + + ^ aBlock value: self +! ! + +!Object methodsFor: 'accessing' stamp: 'sw 10/17/2000 11:15'! +presenter + "Answer the presenter object associated with the receiver. For morphs, there is in effect a clear containment hierarchy of presenters (accessed via their association with PasteUpMorphs); for arbitrary objects the hook is simply via the current world, at least at present." + + ^ self currentWorld presenter! ! + +!Object methodsFor: 'accessing'! +readFromString: aString + "Create an object based on the contents of aString." + + ^self readFrom: (ReadStream on: aString)! ! + +!Object methodsFor: 'accessing' stamp: 'di 3/29/1999 13:10'! +size + "Primitive. Answer the number of indexable variables in the receiver. + This value is the same as the largest legal subscript. Essential. See Object + documentation whatIsAPrimitive." + + + self class isVariable ifFalse: [self errorNotIndexable]. + ^ 0! ! + +!Object methodsFor: 'accessing' stamp: 'md 5/16/2006 12:34'! +yourself + "Answer self." + ^self! ! + + +!Object methodsFor: 'associating' stamp: 'md 7/22/2005 16:03'! +-> anObject + "Answer an Association between self and anObject" + + ^Association basicNew key: self value: anObject! ! + + +!Object methodsFor: 'binding'! +bindingOf: aString + ^nil! ! + + +!Object methodsFor: 'breakpoint' stamp: 'bkv 7/1/2003 12:33'! +break + "This is a simple message to use for inserting breakpoints during debugging. + The debugger is opened by sending a signal. This gives a chance to restore + invariants related to multiple processes." + + BreakPoint signal. + + "nil break."! ! + + +!Object methodsFor: 'casing'! +caseOf: aBlockAssociationCollection + "The elements of aBlockAssociationCollection are associations between blocks. + Answer the evaluated value of the first association in aBlockAssociationCollection + whose evaluated key equals the receiver. If no match is found, report an error." + + ^ self caseOf: aBlockAssociationCollection otherwise: [self caseError] + +"| z | z _ {[#a]->[1+1]. ['b' asSymbol]->[2+2]. [#c]->[3+3]}. #b caseOf: z" +"| z | z _ {[#a]->[1+1]. ['d' asSymbol]->[2+2]. [#c]->[3+3]}. #b caseOf: z" +"The following are compiled in-line:" +"#b caseOf: {[#a]->[1+1]. ['b' asSymbol]->[2+2]. [#c]->[3+3]}" +"#b caseOf: {[#a]->[1+1]. ['d' asSymbol]->[2+2]. [#c]->[3+3]}"! ! + +!Object methodsFor: 'casing'! +caseOf: aBlockAssociationCollection otherwise: aBlock + "The elements of aBlockAssociationCollection are associations between blocks. + Answer the evaluated value of the first association in aBlockAssociationCollection + whose evaluated key equals the receiver. If no match is found, answer the result + of evaluating aBlock." + + aBlockAssociationCollection associationsDo: + [:assoc | (assoc key value = self) ifTrue: [^assoc value value]]. + ^ aBlock value + +"| z | z _ {[#a]->[1+1]. ['b' asSymbol]->[2+2]. [#c]->[3+3]}. #b caseOf: z otherwise: [0]" +"| z | z _ {[#a]->[1+1]. ['d' asSymbol]->[2+2]. [#c]->[3+3]}. #b caseOf: z otherwise: [0]" +"The following are compiled in-line:" +"#b caseOf: {[#a]->[1+1]. ['b' asSymbol]->[2+2]. [#c]->[3+3]} otherwise: [0]" +"#b caseOf: {[#a]->[1+1]. ['d' asSymbol]->[2+2]. [#c]->[3+3]} otherwise: [0]"! ! + + +!Object methodsFor: 'class membership'! +class + "Primitive. Answer the object which is the receiver's class. Essential. See + Object documentation whatIsAPrimitive." + + + self primitiveFailed! ! + +!Object methodsFor: 'class membership' stamp: 'sw 9/27/2001 15:51'! +inheritsFromAnyIn: aList + "Answer whether the receiver inherits from any class represented by any element in the list. The elements of the list can be classes, class name symbols, or strings representing possible class names. This allows speculative membership tests to be made even when some of the classes may not be known to the current image, and even when their names are not interned symbols." + + | aClass | + aList do: + [:elem | Symbol hasInterned: elem asString ifTrue: + [:elemSymbol | (((aClass _ Smalltalk at: elemSymbol ifAbsent: [nil]) isKindOf: Class) + and: [self isKindOf: aClass]) + ifTrue: + [^ true]]]. + ^ false + + +" +{3. true. 'olive'} do: + [:token | + {{#Number. #Boolean}. {Number. Boolean }. {'Number'. 'Boolean'}} do: + [:list | + Transcript cr; show: token asString, ' list element provided as a ', list first class name, ' - ', (token inheritsFromAnyIn: list) asString]] +"! ! + +!Object methodsFor: 'class membership'! +isKindOf: aClass + "Answer whether the class, aClass, is a superclass or class of the receiver." + + self class == aClass + ifTrue: [^true] + ifFalse: [^self class inheritsFrom: aClass]! ! + +!Object methodsFor: 'class membership' stamp: 'sw 2/16/98 02:08'! +isKindOf: aClass orOf: anotherClass + "Answer whether either of the classes, aClass or anotherClass,, is a superclass or class of the receiver. A convenience; could be somewhat optimized" + ^ (self isKindOf: aClass) or: [self isKindOf: anotherClass]! ! + +!Object methodsFor: 'class membership'! +isMemberOf: aClass + "Answer whether the receiver is an instance of the class, aClass." + + ^self class == aClass! ! + +!Object methodsFor: 'class membership'! +respondsTo: aSymbol + "Answer whether the method dictionary of the receiver's class contains + aSymbol as a message selector." + + ^self class canUnderstand: aSymbol! ! + +!Object methodsFor: 'class membership' stamp: 'tk 10/21/1998 12:38'! +xxxClass + "For subclasses of nil, such as ObjectOut" + ^ self class! ! + + +!Object methodsFor: 'comparing' stamp: 'tk 4/16/1999 18:26'! +closeTo: anObject + "Answer whether the receiver and the argument represent the same + object. If = is redefined in any subclass, consider also redefining the + message hash." + + | ans | + [ans _ self = anObject] ifError: [:aString :aReceiver | ^ false]. + ^ ans! ! + +!Object methodsFor: 'comparing'! +hash + "Answer a SmallInteger whose value is related to the receiver's identity. + May be overridden, and should be overridden in any classes that define = " + + ^ self identityHash! ! + +!Object methodsFor: 'comparing' stamp: 'pm 9/23/97 09:36'! +hashMappedBy: map + "Answer what my hash would be if oops changed according to map." + + ^map newHashFor: self! ! + +!Object methodsFor: 'comparing' stamp: 'di 9/27/97 20:23'! +identityHashMappedBy: map + "Answer what my hash would be if oops changed according to map." + + ^map newHashFor: self! ! + +!Object methodsFor: 'comparing' stamp: 'sw 8/20/1998 12:34'! +identityHashPrintString + "'fred' identityHashPrintString" + + ^ '(', self identityHash printString, ')'! ! + +!Object methodsFor: 'comparing' stamp: 'ajh 2/2/2002 15:02'! +literalEqual: other + + ^ self class == other class and: [self = other]! ! + +!Object methodsFor: 'comparing'! += anObject + "Answer whether the receiver and the argument represent the same + object. If = is redefined in any subclass, consider also redefining the + message hash." + + ^self == anObject! ! + +!Object methodsFor: 'comparing'! +~= anObject + "Answer whether the receiver and the argument do not represent the + same object." + + ^self = anObject == false! ! + + +!Object methodsFor: 'converting' stamp: 'di 11/9/1998 12:15'! +adaptToFloat: rcvr andSend: selector + "If no method has been provided for adapting an object to a Float, + then it may be adequate to simply adapt it to a number." + ^ self adaptToNumber: rcvr andSend: selector! ! + +!Object methodsFor: 'converting' stamp: 'di 11/9/1998 12:14'! +adaptToFraction: rcvr andSend: selector + "If no method has been provided for adapting an object to a Fraction, + then it may be adequate to simply adapt it to a number." + ^ self adaptToNumber: rcvr andSend: selector! ! + +!Object methodsFor: 'converting' stamp: 'di 11/9/1998 12:15'! +adaptToInteger: rcvr andSend: selector + "If no method has been provided for adapting an object to a Integer, + then it may be adequate to simply adapt it to a number." + ^ self adaptToNumber: rcvr andSend: selector! ! + +!Object methodsFor: 'converting' stamp: 'rw 4/27/2002 07:48'! +asActionSequence + + ^WeakActionSequence with: self! ! + +!Object methodsFor: 'converting' stamp: 'rw 7/20/2003 16:03'! +asActionSequenceTrappingErrors + + ^WeakActionSequenceTrappingErrors with: self! ! + +!Object methodsFor: 'converting' stamp: 'svp 5/16/2000 18:14'! +asDraggableMorph + ^(StringMorph contents: self printString) + color: Color white; + yourself! ! + +!Object methodsFor: 'converting' stamp: 'sma 5/12/2000 17:39'! +asOrderedCollection + "Answer an OrderedCollection with the receiver as its only element." + + ^ OrderedCollection with: self! ! + +!Object methodsFor: 'converting'! +asString + "Answer a string that represents the receiver." + + ^ self printString ! ! + +!Object methodsFor: 'converting' stamp: 'ajh 3/11/2003 10:27'! +asStringOrText + "Answer a string that represents the receiver." + + ^ self printString ! ! + +!Object methodsFor: 'converting'! +as: aSimilarClass + "Create an object of class aSimilarClass that has similar contents to the receiver." + + ^ aSimilarClass newFrom: self! ! + +!Object methodsFor: 'converting' stamp: 'RAA 8/2/1999 12:41'! +complexContents + + ^self! ! + +!Object methodsFor: 'converting' stamp: 'ajh 7/6/2003 20:37'! +mustBeBoolean + "Catches attempts to test truth of non-Booleans. This message is sent from the VM. The sending context is rewound to just before the jump causing this exception." + + ^ self mustBeBooleanIn: thisContext sender! ! + +!Object methodsFor: 'converting' stamp: 'ajh 7/6/2003 20:40'! +mustBeBooleanIn: context + "context is the where the non-boolean error occurred. Rewind context to before jump then raise error." + + | proceedValue | + context skipBackBeforeJump. + proceedValue _ NonBooleanReceiver new + object: self; + signal: 'proceed for truth.'. + ^ proceedValue ~~ false! ! + +!Object methodsFor: 'converting' stamp: 'sw 3/26/2001 12:12'! +printDirectlyToDisplay + "For debugging: write the receiver's printString directly to the display at (0, 100); senders of this are detected by the check-for-slips mechanism." + + self asString displayAt: 0@100 + +"StringMorph someInstance printDirectlyToDisplay"! ! + +!Object methodsFor: 'converting' stamp: 'RAA 3/31/1999 12:13'! +withoutListWrapper + + ^self! ! + + +!Object methodsFor: 'copying'! +clone + + + self primitiveFailed! ! + +!Object methodsFor: 'copying' stamp: 'ajh 8/18/2001 21:25'! +copy + "Answer another instance just like the receiver. Subclasses typically override postCopy; they typically do not override shallowCopy." + + ^self shallowCopy postCopy! ! + +!Object methodsFor: 'copying' stamp: 'tk 8/20/1998 16:01'! +copyAddedStateFrom: anotherObject + "Copy over the values of instance variables added by the receiver's class from anotherObject to the receiver. These will be remapped in mapUniClasses, if needed." + + self class superclass instSize + 1 to: self class instSize do: + [:index | self instVarAt: index put: (anotherObject instVarAt: index)]! ! + +!Object methodsFor: 'copying' stamp: 'tpr 2/14/2004 21:53'! +copyFrom: anotherObject + "Copy to myself all instance variables I have in common with anotherObject. This is dangerous because it ignores an object's control over its own inst vars. " + + | mine his | + + mine _ self class allInstVarNames. + his _ anotherObject class allInstVarNames. + 1 to: (mine size min: his size) do: [:ind | + (mine at: ind) = (his at: ind) ifTrue: [ + self instVarAt: ind put: (anotherObject instVarAt: ind)]]. + self class isVariable & anotherObject class isVariable ifTrue: [ + 1 to: (self basicSize min: anotherObject basicSize) do: [:ind | + self basicAt: ind put: (anotherObject basicAt: ind)]].! ! + +!Object methodsFor: 'copying' stamp: 'ajh 5/23/2002 00:38'! +copySameFrom: otherObject + "Copy to myself all instance variables named the same in otherObject. + This ignores otherObject's control over its own inst vars." + + | myInstVars otherInstVars match | + myInstVars _ self class allInstVarNames. + otherInstVars _ otherObject class allInstVarNames. + myInstVars doWithIndex: [:each :index | + (match _ otherInstVars indexOf: each) > 0 ifTrue: + [self instVarAt: index put: (otherObject instVarAt: match)]]. + 1 to: (self basicSize min: otherObject basicSize) do: [:i | + self basicAt: i put: (otherObject basicAt: i)]. +! ! + +!Object methodsFor: 'copying' stamp: 'tk 4/20/1999 14:44'! +copyTwoLevel + "one more level than a shallowCopy" + + | newObject class index | + class _ self class. + newObject _ self clone. + newObject == self ifTrue: [^ self]. + class isVariable + ifTrue: + [index _ self basicSize. + [index > 0] + whileTrue: + [newObject basicAt: index put: (self basicAt: index) shallowCopy. + index _ index - 1]]. + index _ class instSize. + [index > 0] + whileTrue: + [newObject instVarAt: index put: (self instVarAt: index) shallowCopy. + index _ index - 1]. + ^newObject! ! + +!Object methodsFor: 'copying'! +deepCopy + "Answer a copy of the receiver with its own copy of each instance + variable." + + | newObject class index | + class _ self class. + (class == Object) ifTrue: [^self]. + class isVariable + ifTrue: + [index _ self basicSize. + newObject _ class basicNew: index. + [index > 0] + whileTrue: + [newObject basicAt: index put: (self basicAt: index) deepCopy. + index _ index - 1]] + ifFalse: [newObject _ class basicNew]. + index _ class instSize. + [index > 0] + whileTrue: + [newObject instVarAt: index put: (self instVarAt: index) deepCopy. + index _ index - 1]. + ^newObject! ! + +!Object methodsFor: 'copying' stamp: 'hg 11/23/1999 13:43'! +initialDeepCopierSize + "default value is 4096; other classes may override this, esp. for smaller (=faster) sizes" + + ^4096! ! + +!Object methodsFor: 'copying' stamp: 'ajh 1/27/2003 18:45'! +postCopy + "self is a shallow copy, subclasses should copy fields as necessary to complete the full copy" + + ^ self! ! + +!Object methodsFor: 'copying' stamp: 'jm 11/14/97 11:08'! +shallowCopy + "Answer a copy of the receiver which shares the receiver's instance variables." + | class newObject index | + + class _ self class. + class isVariable + ifTrue: + [index _ self basicSize. + newObject _ class basicNew: index. + [index > 0] + whileTrue: + [newObject basicAt: index put: (self basicAt: index). + index _ index - 1]] + ifFalse: [newObject _ class basicNew]. + index _ class instSize. + [index > 0] + whileTrue: + [newObject instVarAt: index put: (self instVarAt: index). + index _ index - 1]. + ^ newObject! ! + +!Object methodsFor: 'copying' stamp: 'tk 3/11/2003 13:58'! +veryDeepCopy + "Do a complete tree copy using a dictionary. An object in the tree twice is only copied once. All references to the object in the copy of the tree will point to the new copy." + + | copier new | + copier _ DeepCopier new initialize: self initialDeepCopierSize. + new _ self veryDeepCopyWith: copier. + copier mapUniClasses. + copier references associationsDo: [:assoc | + assoc value veryDeepFixupWith: copier]. + copier fixDependents. + ^ new! ! + +!Object methodsFor: 'copying' stamp: 'tk 3/11/2003 13:58'! +veryDeepCopySibling + "Do a complete tree copy using a dictionary. Substitute a clone of oldPlayer for the root. Normally, a Player or non systemDefined object would have a new class. We do not want one this time. An object in the tree twice, is only copied once. All references to the object in the copy of the tree will point to the new copy." + + | copier new | + copier _ DeepCopier new initialize: self initialDeepCopierSize. + copier newUniClasses: false. + new _ self veryDeepCopyWith: copier. + copier mapUniClasses. + copier references associationsDo: [:assoc | + assoc value veryDeepFixupWith: copier]. + copier fixDependents. + ^ new! ! + +!Object methodsFor: 'copying' stamp: 'tk 5/13/2003 19:39'! +veryDeepCopyUsing: copier + "Do a complete tree copy using a dictionary. An object in the tree twice is only copied once. All references to the object in the copy of the tree will point to the new copy. + Same as veryDeepCopy except copier (with dictionary) is supplied. + ** do not delete this method, even if it has no callers **" + + | new refs newDep newModel | + new _ self veryDeepCopyWith: copier. + copier mapUniClasses. + copier references associationsDo: [:assoc | + assoc value veryDeepFixupWith: copier]. + "Fix dependents" + refs _ copier references. + DependentsFields associationsDo: [:pair | + pair value do: [:dep | + (newDep _ refs at: dep ifAbsent: [nil]) ifNotNil: [ + newModel _ refs at: pair key ifAbsent: [pair key]. + newModel addDependent: newDep]]]. + ^ new! ! + +!Object methodsFor: 'copying' stamp: 'tk 3/11/2003 14:12'! +veryDeepCopyWith: deepCopier + "Copy me and the entire tree of objects I point to. An object in the tree twice is copied once, and both references point to him. deepCopier holds a dictionary of objects we have seen. Some classes refuse to be copied. Some classes are picky about which fields get deep copied." + | class index sub subAss new uc sup has mine | + deepCopier references at: self ifPresent: [:newer | ^ newer]. "already did him" + class _ self class. + class isMeta ifTrue: [^ self]. "a class" + new _ self clone. + (class isSystemDefined not and: [deepCopier newUniClasses "allowed"]) ifTrue: [ + uc _ deepCopier uniClasses at: class ifAbsent: [nil]. + uc ifNil: [ + deepCopier uniClasses at: class put: (uc _ self copyUniClassWith: deepCopier). + deepCopier references at: class put: uc]. "remember" + new _ uc new. + new copyFrom: self]. "copy inst vars in case any are weak" + deepCopier references at: self put: new. "remember" + (class isVariable and: [class isPointers]) ifTrue: + [index _ self basicSize. + [index > 0] whileTrue: + [sub _ self basicAt: index. + (subAss _ deepCopier references associationAt: sub ifAbsent: [nil]) + ifNil: [new basicAt: index put: (sub veryDeepCopyWith: deepCopier)] + ifNotNil: [new basicAt: index put: subAss value]. + index _ index - 1]]. + "Ask each superclass if it wants to share (weak copy) any inst vars" + new veryDeepInner: deepCopier. "does super a lot" + + "other superclasses want all inst vars deep copied" + sup _ class. index _ class instSize. + [has _ sup compiledMethodAt: #veryDeepInner: ifAbsent: [nil]. + has _ has ifNil: [class isSystemDefined not "is a uniClass"] ifNotNil: [true]. + mine _ sup instVarNames. + has ifTrue: [index _ index - mine size] "skip inst vars" + ifFalse: [1 to: mine size do: [:xx | + sub _ self instVarAt: index. + (subAss _ deepCopier references associationAt: sub ifAbsent: [nil]) + "use association, not value, so nil is an exceptional value" + ifNil: [new instVarAt: index put: + (sub veryDeepCopyWith: deepCopier)] + ifNotNil: [new instVarAt: index put: subAss value]. + index _ index - 1]]. + (sup _ sup superclass) == nil] whileFalse. + new rehash. "force Sets and Dictionaries to rehash" + ^ new +! ! + +!Object methodsFor: 'copying' stamp: 'tk 1/6/1999 17:39'! +veryDeepFixupWith: deepCopier + "I have no fields and no superclass. Catch the super call." +! ! + +!Object methodsFor: 'copying' stamp: 'tk 9/4/2001 10:30'! +veryDeepInner: deepCopier + "No special treatment for inst vars of my superclasses. Override when some need to be weakly copied. Object>>veryDeepCopyWith: will veryDeepCopy any inst var whose class does not actually define veryDeepInner:" +! ! + + +!Object methodsFor: 'creation' stamp: 'nk 2/26/2004 13:35'! +asStringMorph + "Open a StringMorph, as best one can, on the receiver" + + ^ self asStringOrText asStringMorph +! ! + +!Object methodsFor: 'creation' stamp: 'nk 2/26/2004 13:35'! +asTextMorph + "Open a TextMorph, as best one can, on the receiver" + + ^ TextMorph new contentsAsIs: self asStringOrText +! ! + +!Object methodsFor: 'creation' stamp: 'sw 1/29/2002 21:45'! +openAsMorph + "Open a morph, as best one can, on the receiver" + + ^ self asMorph openInHand + +" +234 openAsMorph +(ScriptingSystem formAtKey: #TinyMenu) openAsMorph +'fred' openAsMorph +"! ! + + +!Object methodsFor: 'debugging' stamp: 'md 11/24/2004 11:45'! +haltIf: condition + "This is the typical message to use for inserting breakpoints during + debugging. Param can be a block or expression, halt if true. + If the Block has one arg, the receiver is bound to that. + If the condition is a selector, we look up in the callchain. Halt if + any method's selector equals selector." + | cntxt | + + condition isSymbol ifTrue:[ + "only halt if a method with selector symbol is in callchain" + cntxt := thisContext. + [cntxt sender isNil] whileFalse: [ + cntxt := cntxt sender. + (cntxt selector = condition) ifTrue: [Halt signal]. + ]. + ^self. + ]. + (condition isBlock + ifTrue: [condition valueWithPossibleArgument: self] + ifFalse: [condition] + ) ifTrue: [ + Halt signal + ].! ! + +!Object methodsFor: 'debugging'! +needsWork! ! + + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:26'! +checkHaltCountExpired + | counter | + counter _ Smalltalk at: #HaltCount ifAbsent: [0]. + ^counter = 0! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 5/19/2004 19:04'! +clearHaltOnce + "Turn on the halt once flag." + Smalltalk at: #HaltOnce put: false! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:30'! +decrementAndCheckHaltCount + self decrementHaltCount. + ^self checkHaltCountExpired! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:28'! +decrementHaltCount + | counter | + counter := Smalltalk + at: #HaltCount + ifAbsent: [0]. + counter > 0 ifTrue: [ + counter _ counter - 1. + self setHaltCountTo: counter]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:42'! +doExpiredHaltCount + self clearHaltOnce. + self removeHaltCount. + self halt! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:44'! +doExpiredHaltCount: aString + self clearHaltOnce. + self removeHaltCount. + self halt: aString! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:45'! +doExpiredInspectCount + self clearHaltOnce. + self removeHaltCount. + self inspect! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:43'! +haltOnCount: int + self haltOnceEnabled + ifTrue: [self hasHaltCount + ifTrue: [self decrementAndCheckHaltCount + ifTrue: [self doExpiredHaltCount]] + ifFalse: [int = 1 + ifTrue: [self doExpiredHaltCount] + ifFalse: [self setHaltCountTo: int - 1]]]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 5/19/2004 19:05'! +haltOnce + "Halt unless we have already done it once." + self haltOnceEnabled + ifTrue: [self clearHaltOnce. + ^ self halt]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 5/19/2004 19:04'! +haltOnceEnabled + ^ Smalltalk + at: #HaltOnce + ifAbsent: [false]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 5/19/2004 19:05'! +haltOnce: aString + "Halt unless we have already done it once." + self haltOnceEnabled + ifTrue: [self clearHaltOnce. + ^ self halt: aString]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:45'! +halt: aString onCount: int + self haltOnceEnabled + ifTrue: [self hasHaltCount + ifTrue: [self decrementAndCheckHaltCount + ifTrue: [self doExpiredHaltCount: aString]] + ifFalse: [int = 1 + ifTrue: [self doExpiredHaltCount: aString] + ifFalse: [self setHaltCountTo: int - 1]]]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:36'! +hasHaltCount + ^Smalltalk + includesKey: #HaltCount! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:46'! +inspectOnCount: int + self haltOnceEnabled + ifTrue: [self hasHaltCount + ifTrue: [self decrementAndCheckHaltCount + ifTrue: [self doExpiredInspectCount]] + ifFalse: [int = 1 + ifTrue: [self doExpiredInspectCount] + ifFalse: [self setHaltCountTo: int - 1]]]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 5/19/2004 19:05'! +inspectOnce + "Inspect unless we have already done it once." + self haltOnceEnabled + ifTrue: [self clearHaltOnce. + ^ self inspect]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 13:20'! +inspectUntilCount: int + self haltOnceEnabled + ifTrue: [self hasHaltCount + ifTrue: [self decrementAndCheckHaltCount + ifTrue: [self doExpiredInspectCount] + ifFalse: [self inspect]] + ifFalse: [int = 1 + ifTrue: [self doExpiredInspectCount] + ifFalse: [self setHaltCountTo: int - 1]]]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:49'! +removeHaltCount + (Smalltalk includesKey: #HaltCount) ifTrue: [ + Smalltalk removeKey: #HaltCount]! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 6/2/2004 08:25'! +setHaltCountTo: int + Smalltalk at: #HaltCount put: int! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 5/19/2004 19:04'! +setHaltOnce + "Turn on the halt once flag." + Smalltalk at: #HaltOnce put: true! ! + +!Object methodsFor: 'debugging-haltOnce' stamp: 'sbw 5/19/2004 19:04'! +toggleHaltOnce + self haltOnceEnabled + ifTrue: [self clearHaltOnce] + ifFalse: [self setHaltOnce]! ! + + +!Object methodsFor: 'dependents access' stamp: 'ar 2/11/2001 01:55'! +addDependent: anObject + "Make the given object one of the receiver's dependents." + + | dependents | + dependents _ self dependents. + (dependents includes: anObject) ifFalse: + [self myDependents: (dependents copyWithDependent: anObject)]. + ^ anObject! ! + +!Object methodsFor: 'dependents access' stamp: 'sma 2/29/2000 19:53'! +breakDependents + "Remove all of the receiver's dependents." + + self myDependents: nil! ! + +!Object methodsFor: 'dependents access' stamp: 'sma 2/29/2000 19:26'! +canDiscardEdits + "Answer true if none of the views on this model has unaccepted edits that matter." + + self dependents + do: [:each | each canDiscardEdits ifFalse: [^ false]] + without: self. + ^ true! ! + +!Object methodsFor: 'dependents access' stamp: 'sma 2/29/2000 19:58'! +dependents + "Answer a collection of objects that are 'dependent' on the receiver; + that is, all objects that should be notified if the receiver changes." + + ^ self myDependents ifNil: [#()]! ! + +!Object methodsFor: 'dependents access'! +evaluate: actionBlock wheneverChangeIn: aspectBlock + | viewerThenObject objectThenViewer | + objectThenViewer _ self. + viewerThenObject _ ObjectViewer on: objectThenViewer. + objectThenViewer become: viewerThenObject. + "--- Then ---" + objectThenViewer xxxViewedObject: viewerThenObject + evaluate: actionBlock + wheneverChangeIn: aspectBlock! ! + +!Object methodsFor: 'dependents access' stamp: 'sma 2/29/2000 19:59'! +hasUnacceptedEdits + "Answer true if any of the views on this object has unaccepted edits." + + self dependents + do: [:each | each hasUnacceptedEdits ifTrue: [^ true]] + without: self. + ^ false! ! + +!Object methodsFor: 'dependents access' stamp: 'sma 2/29/2000 19:55'! +myDependents + "Private. Answer a list of all the receiver's dependents." + + ^ DependentsFields at: self ifAbsent: []! ! + +!Object methodsFor: 'dependents access' stamp: 'sma 2/29/2000 19:52'! +myDependents: aCollectionOrNil + "Private. Set (or remove) the receiver's dependents list." + + aCollectionOrNil + ifNil: [DependentsFields removeKey: self ifAbsent: []] + ifNotNil: [DependentsFields at: self put: aCollectionOrNil]! ! + +!Object methodsFor: 'dependents access' stamp: 'reThink 2/18/2001 17:06'! +release + "Remove references to objects that may refer to the receiver. This message + should be overridden by subclasses with any cycles, in which case the + subclass should also include the expression super release." + + self releaseActionMap! ! + +!Object methodsFor: 'dependents access' stamp: 'sma 2/29/2000 20:23'! +removeDependent: anObject + "Remove the given object as one of the receiver's dependents." + + | dependents | + dependents _ self dependents reject: [:each | each == anObject]. + self myDependents: (dependents isEmpty ifFalse: [dependents]). + ^ anObject! ! + + +!Object methodsFor: 'drag and drop' stamp: 'bh 9/16/2001 18:10'! +acceptDroppingMorph: transferMorph event: evt inMorph: dstListMorph + + ^false.! ! + +!Object methodsFor: 'drag and drop' stamp: 'mir 5/16/2000 11:35'! +dragAnimationFor: item transferMorph: transferMorph + "Default do nothing"! ! + +!Object methodsFor: 'drag and drop' stamp: 'panda 4/28/2000 16:20'! +dragPassengerFor: item inMorph: dragSource + ^item! ! + +!Object methodsFor: 'drag and drop' stamp: 'panda 4/28/2000 16:11'! +dragTransferType + ^nil! ! + +!Object methodsFor: 'drag and drop' stamp: 'panda 4/28/2000 16:05'! +dragTransferTypeForMorph: dragSource + ^nil! ! + +!Object methodsFor: 'drag and drop' stamp: 'mir 5/8/2000 17:19'! +wantsDroppedMorph: aMorph event: anEvent inMorph: destinationLM + ^false! ! + + +!Object methodsFor: 'error handling' stamp: 'sma 5/6/2000 19:35'! +assert: aBlock + "Throw an assertion error if aBlock does not evaluates to true." + + aBlock value ifFalse: [AssertionFailure signal: 'Assertion failed']! ! + +!Object methodsFor: 'error handling' stamp: 'nk 1/15/2004 10:54'! +assert: aBlock descriptionBlock: descriptionBlock + "Throw an assertion error if aBlock does not evaluate to true." + + aBlock value ifFalse: [AssertionFailure signal: descriptionBlock value asString ]! ! + +!Object methodsFor: 'error handling' stamp: 'nk 10/25/2003 16:47'! +assert: aBlock description: aString + "Throw an assertion error if aBlock does not evaluates to true." + + aBlock value ifFalse: [AssertionFailure signal: aString ]! ! + +!Object methodsFor: 'error handling' stamp: 'md 10/13/2004 15:59'! +backwardCompatibilityOnly: anExplanationString + "Warn that the sending method has been deprecated. Methods that are tagt with #backwardCompatibility: + are kept for compatibility." + + Preferences showDeprecationWarnings ifTrue: + [Deprecation signal: thisContext sender printString, ' has been deprecated (but will be kept for compatibility). ', anExplanationString]! ! + +!Object methodsFor: 'error handling'! +caseError + "Report an error from an in-line or explicit case statement." + + self error: 'Case not found, and no otherwise clause'! ! + +!Object methodsFor: 'error handling' stamp: 'rbb 3/1/2005 09:26'! +confirm: queryString + "Put up a yes/no menu with caption queryString. Answer true if the + response is yes, false if no. This is a modal question--the user must + respond yes or no." + + "nil confirm: 'Are you hungry?'" + + ^ UIManager default confirm: queryString! ! + +!Object methodsFor: 'error handling' stamp: 'rbb 3/1/2005 09:27'! +confirm: aString orCancel: cancelBlock + "Put up a yes/no/cancel menu with caption aString. Answer true if + the response is yes, false if no. If cancel is chosen, evaluate + cancelBlock. This is a modal question--the user must respond yes or no." + + ^ UIManager default confirm: aString orCancel: cancelBlock! ! + +!Object methodsFor: 'error handling' stamp: 'dew 10/6/2003 18:20'! +deprecated: anExplanationString + "Warn that the sending method has been deprecated." + + Preferences showDeprecationWarnings ifTrue: + [Deprecation signal: thisContext sender printString, ' has been deprecated. ', anExplanationString]! ! + +!Object methodsFor: 'error handling' stamp: 'dew 10/7/2003 00:26'! +deprecated: anExplanationString block: aBlock + "Warn that the sender has been deprecated. Answer the value of aBlock on resumption. (Note that #deprecated: is usually the preferred method.)" + + Preferences showDeprecationWarnings ifTrue: + [Deprecation + signal: thisContext sender printString, ' has been deprecated. ', anExplanationString]. + ^ aBlock value. +! ! + +!Object methodsFor: 'error handling' stamp: 'md 2/22/2006 21:21'! +doesNotUnderstand: aMessage + "Handle the fact that there was an attempt to send the given message to the receiver but the receiver does not understand this message (typically sent from the machine when a message is sent to the receiver and no method is defined for that selector)." + "Testing: (3 activeProcess)" + + MessageNotUnderstood new + message: aMessage; + receiver: self; + signal. + ^ aMessage sentTo: self. +! ! + +!Object methodsFor: 'error handling' stamp: 'TRee 11/4/2003 16:47'! +dpsTrace: reportObject + Transcript myDependents isNil ifTrue: [^self]. + self dpsTrace: reportObject levels: 1 withContext: thisContext + +" nil dpsTrace: 'sludder'. "! ! + +!Object methodsFor: 'error handling' stamp: 'TRee 11/4/2003 16:49'! +dpsTrace: reportObject levels: anInt + self dpsTrace: reportObject levels: anInt withContext: thisContext + +"(1 to: 3) do: [:int | nil dpsTrace: int levels: 5.]"! ! + +!Object methodsFor: 'error handling' stamp: 'TRee 11/4/2003 17:02'! +dpsTrace: reportObject levels: anInt withContext: currentContext + | reportString context displayCount | + reportString := (reportObject respondsTo: #asString) + ifTrue: [reportObject asString] ifFalse: [reportObject printString]. + (Smalltalk at: #Decompiler ifAbsent: [nil]) + ifNil: + [Transcript cr; show: reportString] + ifNotNil: + [context := currentContext. + displayCount := anInt > 1. + 1 to: anInt do: + [:count | + Transcript cr. + displayCount + ifTrue: [Transcript show: count printString, ': ']. + + reportString notNil + ifTrue: + [Transcript show: context home class name + , '/' , context sender selector, ' (' , reportString , ')'. + context := context sender. + reportString := nil] + ifFalse: + [(context notNil and: [(context := context sender) notNil]) + ifTrue: [Transcript show: context receiver class name , '/' , context selector]]]. + "Transcript cr"].! ! + +!Object methodsFor: 'error handling' stamp: 'md 8/2/2005 22:17'! +error + "Throw a generic Error exception." + + ^self error: 'Error!!'.! ! + +!Object methodsFor: 'error handling' stamp: 'tfei 4/12/1999 12:55'! +error: aString + "Throw a generic Error exception." + + ^Error new signal: aString! ! + +!Object methodsFor: 'error handling' stamp: 'al 9/16/2005 14:12'! +explicitRequirement + self error: 'Explicitly required method'! ! + +!Object methodsFor: 'error handling' stamp: 'al 2/13/2006 22:20'! +halt + "This is the typical message to use for inserting breakpoints during + debugging. It behaves like halt:, but does not call on halt: in order to + avoid putting this message on the stack. Halt is especially useful when + the breakpoint message is an arbitrary one." + + Halt signal! ! + +!Object methodsFor: 'error handling' stamp: 'tfei 4/12/1999 12:59'! +halt: aString + "This is the typical message to use for inserting breakpoints during + debugging. It creates and schedules a Notifier with the argument, + aString, as the label." + + Halt new signal: aString! ! + +!Object methodsFor: 'error handling' stamp: 'md 1/20/2006 16:24'! +handles: exception + "This method exists in case a non exception class is the first arg in an on:do: (for instance using a exception class that is not loaded). We prefer this to raising an error during error handling itself. Also, semantically it makes sense that the exception handler is not active if its exception class is not loaded" + + ^ false! ! + +!Object methodsFor: 'error handling' stamp: 'ar 9/27/2005 20:24'! +notifyWithLabel: aString + "Create and schedule a Notifier with aString as the window label as well as the contents of the window, in order to request confirmation before a process can proceed." + + ToolSet + debugContext: thisContext + label: aString + contents: aString + + "nil notifyWithLabel: 'let us see if this works'"! ! + +!Object methodsFor: 'error handling' stamp: 'hg 10/2/2001 20:49'! +notify: aString + "Create and schedule a Notifier with the argument as the message in + order to request confirmation before a process can proceed." + + Warning signal: aString + + "nil notify: 'confirmation message'"! ! + +!Object methodsFor: 'error handling'! +notify: aString at: location + "Create and schedule a Notifier with the argument as the message in + order to request confirmation before a process can proceed. Subclasses can + override this and insert an error message at location within aString." + + self notify: aString + + "nil notify: 'confirmation message' at: 12"! ! + +!Object methodsFor: 'error handling'! +primitiveFailed + "Announce that a primitive has failed and there is no appropriate + Smalltalk code to run." + + self error: 'a primitive has failed'! ! + +!Object methodsFor: 'error handling' stamp: 'al 9/16/2005 14:12'! +requirement + self error: 'Implicitly required method'! ! + +!Object methodsFor: 'error handling' stamp: 'AFi 2/8/2003 22:52'! +shouldBeImplemented + "Announce that this message should be implemented" + + self error: 'This message should be implemented'! ! + +!Object methodsFor: 'error handling'! +shouldNotImplement + "Announce that, although the receiver inherits this message, it should + not implement it." + + self error: 'This message is not appropriate for this object'! ! + +!Object methodsFor: 'error handling' stamp: 'md 2/17/2006 12:02'! +subclassResponsibility + "This message sets up a framework for the behavior of the class' subclasses. + Announce that the subclass should have implemented this message." + + self error: 'My subclass should have overridden ', thisContext sender selector printString! ! + +!Object methodsFor: 'error handling' stamp: 'al 12/16/2003 16:16'! +traitConflict + self error: 'A class or trait does not properly resolve a conflict between multiple traits it uses.'! ! + + +!Object methodsFor: 'evaluating' stamp: 'reThink 3/12/2001 18:14'! +value + + ^self! ! + +!Object methodsFor: 'evaluating' stamp: 'reThink 2/18/2001 15:23'! +valueWithArguments: aSequenceOfArguments + + ^self! ! + + +!Object methodsFor: 'events' stamp: 'nk 8/27/2003 16:23'! +actionsWithReceiver: anObject forEvent: anEventSelector + + ^(self actionSequenceForEvent: anEventSelector) + select: [:anAction | anAction receiver == anObject ]! ! + +!Object methodsFor: 'events' stamp: 'nk 8/27/2003 17:45'! +renameActionsWithReceiver: anObject forEvent: anEventSelector toEvent: newEvent + + | oldActions newActions | + oldActions _ Set new. + newActions _ Set new. + (self actionSequenceForEvent: anEventSelector) do: [ :action | + action receiver == anObject + ifTrue: [ oldActions add: anObject ] + ifFalse: [ newActions add: anObject ]]. + self setActionSequence: (ActionSequence withAll: newActions) forEvent: anEventSelector. + oldActions do: [ :act | self when: newEvent evaluate: act ].! ! + + +!Object methodsFor: 'events-accessing' stamp: 'nk 12/20/2002 17:48'! +actionForEvent: anEventSelector + "Answer the action to be evaluated when has been triggered." + + | actions | + actions := self actionMap + at: anEventSelector asSymbol + ifAbsent: [nil]. + actions ifNil: [^nil]. + ^ actions asMinimalRepresentation! ! + +!Object methodsFor: 'events-accessing' stamp: 'nk 12/20/2002 17:48'! +actionForEvent: anEventSelector +ifAbsent: anExceptionBlock + "Answer the action to be evaluated when has been triggered." + + | actions | + actions := self actionMap + at: anEventSelector asSymbol + ifAbsent: [nil]. + actions ifNil: [^anExceptionBlock value]. + ^ actions asMinimalRepresentation! ! + +!Object methodsFor: 'events-accessing' stamp: 'reThink 2/18/2001 14:43'! +actionMap + + ^EventManager actionMapFor: self! ! + +!Object methodsFor: 'events-accessing' stamp: 'rw 4/27/2002 08:35'! +actionSequenceForEvent: anEventSelector + + ^(self actionMap + at: anEventSelector asSymbol + ifAbsent: [^WeakActionSequence new]) + asActionSequence! ! + +!Object methodsFor: 'events-accessing' stamp: 'SqR 6/28/2001 13:19'! +actionsDo: aBlock + + self actionMap do: aBlock! ! + +!Object methodsFor: 'events-accessing' stamp: 'rw 2/10/2002 13:05'! +createActionMap + + ^IdentityDictionary new! ! + +!Object methodsFor: 'events-accessing' stamp: 'SqR 2/19/2001 14:04'! +hasActionForEvent: anEventSelector + "Answer true if there is an action associated with anEventSelector" + + ^(self actionForEvent: anEventSelector) notNil! ! + +!Object methodsFor: 'events-accessing' stamp: 'reThink 2/18/2001 15:29'! +setActionSequence: actionSequence +forEvent: anEventSelector + + | action | + action := actionSequence asMinimalRepresentation. + action == nil + ifTrue: + [self removeActionsForEvent: anEventSelector] + ifFalse: + [self updateableActionMap + at: anEventSelector asSymbol + put: action]! ! + +!Object methodsFor: 'events-accessing' stamp: 'reThink 2/25/2001 08:50'! +updateableActionMap + + ^EventManager updateableActionMapFor: self! ! + + +!Object methodsFor: 'events-registering' stamp: 'reThink 2/18/2001 15:04'! +when: anEventSelector evaluate: anAction + + | actions | + actions := self actionSequenceForEvent: anEventSelector. + (actions includes: anAction) + ifTrue: [^ self]. + self + setActionSequence: (actions copyWith: anAction) + forEvent: anEventSelector! ! + +!Object methodsFor: 'events-registering' stamp: 'rww 12/30/2002 10:37'! +when: anEventSelector +send: aMessageSelector +to: anObject + + self + when: anEventSelector + evaluate: (WeakMessageSend + receiver: anObject + selector: aMessageSelector)! ! + +!Object methodsFor: 'events-registering' stamp: 'rww 12/30/2002 10:37'! +when: anEventSelector +send: aMessageSelector +to: anObject +withArguments: anArgArray + + self + when: anEventSelector + evaluate: (WeakMessageSend + receiver: anObject + selector: aMessageSelector + arguments: anArgArray)! ! + +!Object methodsFor: 'events-registering' stamp: 'rww 12/30/2002 10:37'! +when: anEventSelector +send: aMessageSelector +to: anObject +with: anArg + + self + when: anEventSelector + evaluate: (WeakMessageSend + receiver: anObject + selector: aMessageSelector + arguments: (Array with: anArg))! ! + + +!Object methodsFor: 'events-removing' stamp: 'reThink 2/18/2001 15:33'! +releaseActionMap + + EventManager releaseActionMapFor: self! ! + +!Object methodsFor: 'events-removing' stamp: 'reThink 2/18/2001 15:33'! +removeActionsForEvent: anEventSelector + + | map | + map := self actionMap. + map removeKey: anEventSelector asSymbol ifAbsent: []. + map isEmpty + ifTrue: [self releaseActionMap]! ! + +!Object methodsFor: 'events-removing' stamp: 'nk 8/25/2003 21:46'! +removeActionsSatisfying: aBlock + + self actionMap keys do: + [:eachEventSelector | + self + removeActionsSatisfying: aBlock + forEvent: eachEventSelector + ]! ! + +!Object methodsFor: 'events-removing' stamp: 'reThink 2/18/2001 15:31'! +removeActionsSatisfying: aOneArgBlock +forEvent: anEventSelector + + self + setActionSequence: + ((self actionSequenceForEvent: anEventSelector) + reject: [:anAction | aOneArgBlock value: anAction]) + forEvent: anEventSelector! ! + +!Object methodsFor: 'events-removing' stamp: 'rw 7/29/2003 17:18'! +removeActionsWithReceiver: anObject + + self actionMap copy keysDo: + [:eachEventSelector | + self + removeActionsSatisfying: [:anAction | anAction receiver == anObject] + forEvent: eachEventSelector + ]! ! + +!Object methodsFor: 'events-removing' stamp: 'reThink 2/18/2001 15:36'! +removeActionsWithReceiver: anObject +forEvent: anEventSelector + + self + removeActionsSatisfying: + [:anAction | + anAction receiver == anObject] + forEvent: anEventSelector! ! + +!Object methodsFor: 'events-removing' stamp: 'reThink 2/18/2001 15:31'! +removeAction: anAction +forEvent: anEventSelector + + self + removeActionsSatisfying: [:action | action = anAction] + forEvent: anEventSelector! ! + + +!Object methodsFor: 'events-triggering' stamp: 'reThink 2/18/2001 15:22'! +triggerEvent: anEventSelector + "Evaluate all actions registered for . Return the value of the last registered action." + + ^(self actionForEvent: anEventSelector) value! ! + +!Object methodsFor: 'events-triggering' stamp: 'reThink 2/18/2001 17:09'! +triggerEvent: anEventSelector +ifNotHandled: anExceptionBlock + "Evaluate all actions registered for . Return the value of the last registered action." + + ^(self + actionForEvent: anEventSelector + ifAbsent: [^anExceptionBlock value]) value +! ! + +!Object methodsFor: 'events-triggering' stamp: 'reThink 2/18/2001 15:21'! +triggerEvent: anEventSelector +withArguments: anArgumentList + + ^(self actionForEvent: anEventSelector) + valueWithArguments: anArgumentList! ! + +!Object methodsFor: 'events-triggering' stamp: 'reThink 2/18/2001 15:21'! +triggerEvent: anEventSelector +withArguments: anArgumentList +ifNotHandled: anExceptionBlock + + ^(self + actionForEvent: anEventSelector + ifAbsent: [^anExceptionBlock value]) + valueWithArguments: anArgumentList! ! + +!Object methodsFor: 'events-triggering' stamp: 'reThink 2/18/2001 14:59'! +triggerEvent: anEventSelector +with: anObject + + ^self + triggerEvent: anEventSelector + withArguments: (Array with: anObject)! ! + +!Object methodsFor: 'events-triggering' stamp: 'reThink 2/18/2001 14:59'! +triggerEvent: anEventSelector +with: anObject +ifNotHandled: anExceptionBlock + + ^self + triggerEvent: anEventSelector + withArguments: (Array with: anObject) + ifNotHandled: anExceptionBlock! ! + + +!Object methodsFor: 'filter streaming' stamp: 'MPW 1/1/1901 00:42'! +byteEncode:aStream + self flattenOnStream:aStream. +! ! + +!Object methodsFor: 'filter streaming'! +drawOnCanvas:aStream + self flattenOnStream:aStream. +! ! + +!Object methodsFor: 'filter streaming' stamp: 'MPW 1/1/1901 01:31'! +elementSeparator + ^nil.! ! + +!Object methodsFor: 'filter streaming'! +encodePostscriptOn:aStream + self byteEncode:aStream. +! ! + +!Object methodsFor: 'filter streaming' stamp: 'MPW 1/1/1901 00:07'! +flattenOnStream:aStream + self writeOnFilterStream:aStream. +! ! + +!Object methodsFor: 'filter streaming' stamp: 'mpw 6/22/1930 22:56'! +fullDrawPostscriptOn:aStream + ^aStream fullDraw:self. +! ! + +!Object methodsFor: 'filter streaming' stamp: 'MPW 1/1/1901 01:51'! +printOnStream:aStream + self byteEncode:aStream. +! ! + +!Object methodsFor: 'filter streaming' stamp: 'MPW 1/1/1901 00:49'! +putOn:aStream + ^aStream nextPut:self. +! ! + +!Object methodsFor: 'filter streaming' stamp: 'MPW 1/1/1901 01:53'! +storeOnStream:aStream + self printOnStream:aStream. +! ! + +!Object methodsFor: 'filter streaming' stamp: 'MPW 1/1/1901 00:06'! +writeOnFilterStream:aStream + aStream writeObject:self. +! ! + + +!Object methodsFor: 'finalization' stamp: 'ar 3/21/98 16:26'! +actAsExecutor + "Prepare the receiver to act as executor for any resources associated with it" + self breakDependents! ! + +!Object methodsFor: 'finalization' stamp: 'ar 3/20/98 22:19'! +executor + "Return an object which can act as executor for finalization of the receiver" + ^self shallowCopy actAsExecutor! ! + +!Object methodsFor: 'finalization' stamp: 'ar 5/19/2003 20:10'! +finalizationRegistry + "Answer the finalization registry associated with the receiver." + ^WeakRegistry default! ! + +!Object methodsFor: 'finalization' stamp: 'ar 3/21/98 16:27'! +finalize + "Finalize the resource associated with the receiver. This message should only be sent during the finalization process. There is NO garantuee that the resource associated with the receiver hasn't been free'd before so take care that you don't run into trouble - this all may happen with interrupt priority."! ! + +!Object methodsFor: 'finalization' stamp: 'ar 3/21/98 18:38'! +retryWithGC: execBlock until: testBlock + "Retry execBlock as long as testBlock returns false. Do an incremental GC after the first try, a full GC after the second try." + | blockValue | + blockValue := execBlock value. + (testBlock value: blockValue) ifTrue:[^blockValue]. + Smalltalk garbageCollectMost. + blockValue := execBlock value. + (testBlock value: blockValue) ifTrue:[^blockValue]. + Smalltalk garbageCollect. + ^execBlock value.! ! + +!Object methodsFor: 'finalization' stamp: 'ar 5/19/2003 20:14'! +toFinalizeSend: aSelector to: aFinalizer with: aResourceHandle + "When I am finalized (e.g., garbage collected) close the associated resource handle by sending aSelector to the appropriate finalizer (the guy who knows how to get rid of the resource). + WARNING: Neither the finalizer nor the resource handle are allowed to reference me. If they do, then I will NEVER be garbage collected. Since this cannot be validated here, it is up to the client to make sure this invariant is not broken." + self == aFinalizer ifTrue:[self error: 'I cannot finalize myself']. + self == aResourceHandle ifTrue:[self error: 'I cannot finalize myself']. + ^self finalizationRegistry add: self executor: + (ObjectFinalizer new + receiver: aFinalizer + selector: aSelector + argument: aResourceHandle)! ! + + +!Object methodsFor: 'flagging' stamp: 'sw 8/4/97 16:49'! +isThisEverCalled + ^ self isThisEverCalled: thisContext sender printString! ! + +!Object methodsFor: 'flagging'! +isThisEverCalled: msg + "Send this message, with some useful printable argument, from methods or branches of methods which you believe are never reached. 2/5/96 sw" + + self halt: 'This is indeed called: ', msg printString! ! + +!Object methodsFor: 'flagging' stamp: 'jm 3/18/98 17:23'! +logEntry + + Transcript show: 'Entered ', thisContext sender printString; cr. +! ! + +!Object methodsFor: 'flagging' stamp: 'jm 3/18/98 17:23'! +logExecution + + Transcript show: 'Executing ', thisContext sender printString; cr. +! ! + +!Object methodsFor: 'flagging' stamp: 'jm 3/18/98 17:22'! +logExit + + Transcript show: 'Exited ', thisContext sender printString; cr. +! ! + + +!Object methodsFor: 'graph model' stamp: 'dgd 9/18/2004 15:07'! +addModelYellowButtonMenuItemsTo: aCustomMenu forMorph: aMorph hand: aHandMorph + "The receiver serves as the model for aMorph; a menu is being constructed for the morph, and here the receiver is able to add its own items" + Preferences cmdGesturesEnabled ifTrue: [ "build mode" + aCustomMenu add: 'inspect model' translated target: self action: #inspect. + ]. + + ^aCustomMenu +! ! + +!Object methodsFor: 'graph model' stamp: 'nk 1/23/2004 14:35'! +hasModelYellowButtonMenuItems + ^Preferences cmdGesturesEnabled! ! + + +!Object methodsFor: 'inspecting' stamp: 'ar 9/27/2005 18:31'! +basicInspect + "Create and schedule an Inspector in which the user can examine the + receiver's variables. This method should not be overriden." + ^ToolSet basicInspect: self! ! + +!Object methodsFor: 'inspecting' stamp: 'md 1/18/2006 19:09'! +inspect + "Create and schedule an Inspector in which the user can examine the receiver's variables." + ToolSet inspect: self! ! + +!Object methodsFor: 'inspecting' stamp: 'apb 7/14/2004 12:19'! +inspectorClass + "Answer the class of the inspector to be used on the receiver. Called by inspect; + use basicInspect to get a normal (less useful) type of inspector." + + ^ Inspector! ! + + +!Object methodsFor: 'locales' stamp: 'tak 8/4/2005 14:55'! +localeChanged + self shouldBeImplemented! ! + + +!Object methodsFor: 'macpal' stamp: 'sw 5/7/1998 23:00'! +codeStrippedOut: messageString + "When a method is stripped out for external release, it is replaced by a method that calls this" + + self halt: 'Code stripped out -- ', messageString, '-- do not proceed.'! ! + +!Object methodsFor: 'macpal' stamp: 'sw 1/28/1999 17:31'! +contentsChanged + self changed: #contents! ! + +!Object methodsFor: 'macpal' stamp: 'ar 3/18/2001 00:03'! +currentEvent + "Answer the current Morphic event. This method never returns nil." + ^ActiveEvent ifNil:[self currentHand lastEvent]! ! + +!Object methodsFor: 'macpal' stamp: 'nk 9/1/2004 10:41'! +currentHand + "Return a usable HandMorph -- the one associated with the object's current environment. This method will always return a hand, even if it has to conjure one up as a last resort. If a particular hand is actually handling events at the moment (such as a remote hand or a ghost hand), it will be returned." + + ^ActiveHand ifNil: [ self currentWorld primaryHand ]! ! + +!Object methodsFor: 'macpal' stamp: 'sw 5/17/2001 12:08'! +currentVocabulary + "Answer the currently-prevailing default vocabulary." + + ^ Smalltalk isMorphic ifTrue: + [ActiveWorld currentVocabulary] + ifFalse: + [Vocabulary fullVocabulary]! ! + +!Object methodsFor: 'macpal' stamp: 'ar 3/18/2001 00:08'! +currentWorld + "Answer a morphic world that is the current UI focus. + If in an embedded world, it's that world. + If in a morphic project, it's that project's world. + If in an mvc project, it is the topmost morphic-mvc-window's worldMorph. + If in an mvc project that has no morphic-mvc-windows, then it's just some existing worldmorph instance. + If in an mvc project in a Squeak that has NO WorldMorph instances, one is created. + + This method will never return nil, it will always return its best effort at returning a relevant world morph, but if need be -- if there are no worlds anywhere, it will create a new one." + + | aView aSubview | + ActiveWorld ifNotNil:[^ActiveWorld]. + World ifNotNil:[^World]. + aView _ ScheduledControllers controllerSatisfying: + [:ctrl | (aSubview _ ctrl view firstSubView) notNil and: + [aSubview model isMorph and: [aSubview model isWorldMorph]]]. + ^aView + ifNotNil: + [aSubview model] + ifNil: + [MVCWiWPasteUpMorph newWorldForProject: nil].! ! + +!Object methodsFor: 'macpal' stamp: 'jm 5/6/1998 22:35'! +flash + "Do nothing." +! ! + +!Object methodsFor: 'macpal' stamp: 'sw 6/16/1998 15:07'! +instanceVariableValues + "Answer a collection whose elements are the values of those instance variables of the receiver which were added by the receiver's class" + | c | + c _ OrderedCollection new. + self class superclass instSize + 1 to: self class instSize do: + [:i | c add: (self instVarAt: i)]. + ^ c! ! + +!Object methodsFor: 'macpal' stamp: 'sw 3/20/2001 13:29'! +isUniversalTiles + "Return true if I (my world) uses universal tiles. This message can be called in places where the current World is not known, such as when writing out a project. For more information about the project-writing subtlety addressed by this protocol, kindly contact Ted Kaehler." + + ^ Preferences universalTiles! ! + +!Object methodsFor: 'macpal' stamp: 'sw 10/24/2000 07:04'! +objectRepresented + "most objects represent themselves; this provides a hook for aliases to grab on to" + + ^ self! ! + +!Object methodsFor: 'macpal' stamp: 'sw 5/22/2001 18:31'! +refusesToAcceptCode + "Answer whether the receiver is a code-bearing instrument which at the moment refuses to allow its contents to be submitted" + + ^ false + ! ! + +!Object methodsFor: 'macpal' stamp: 'jm 2/24/1999 12:40'! +scriptPerformer + + ^ self +! ! + +!Object methodsFor: 'macpal' stamp: 'sw 3/20/2001 13:40'! +slotInfo + "Answer a list of slot-information objects. Initally only provides useful info for players" + + ^ Dictionary new! ! + + +!Object methodsFor: 'message handling' stamp: 'md 1/20/2006 16:28'! +executeMethod: compiledMethod + "Execute compiledMethod against the receiver with no args" + + "" "uncomment once prim 189 is in VM" + ^ self withArgs: #() executeMethod: compiledMethod! ! + +!Object methodsFor: 'message handling' stamp: 'di 3/26/1999 07:52'! +perform: aSymbol + "Send the unary selector, aSymbol, to the receiver. + Fail if the number of arguments expected by the selector is not zero. + Primitive. Optional. See Object documentation whatIsAPrimitive." + + + ^ self perform: aSymbol withArguments: (Array new: 0)! ! + +!Object methodsFor: 'message handling' stamp: 'st 11/5/2004 16:19'! +perform: selector orSendTo: otherTarget + "If I wish to intercept and handle selector myself, do it; else send it to otherTarget" + ^ (self respondsTo: selector) ifTrue: [self perform: selector] ifFalse: [otherTarget perform: selector]! ! + +!Object methodsFor: 'message handling' stamp: 'di 3/26/1999 07:55'! +perform: selector withArguments: argArray + "Send the selector, aSymbol, to the receiver with arguments in argArray. + Fail if the number of arguments expected by the selector + does not match the size of argArray. + Primitive. Optional. See Object documentation whatIsAPrimitive." + + + ^ self perform: selector withArguments: argArray inSuperclass: self class! ! + +!Object methodsFor: 'message handling' stamp: 'ar 4/25/2005 13:35'! +perform: selector withArguments: argArray inSuperclass: lookupClass + "NOTE: This is just like perform:withArguments:, except that + the message lookup process begins, not with the receivers's class, + but with the supplied superclass instead. It will fail if lookupClass + cannot be found among the receiver's superclasses. + Primitive. Essential. See Object documentation whatIsAPrimitive." + + + (selector isSymbol) + ifFalse: [^ self error: 'selector argument must be a Symbol']. + (selector numArgs = argArray size) + ifFalse: [^ self error: 'incorrect number of arguments']. + (self class == lookupClass or: [self class inheritsFrom: lookupClass]) + ifFalse: [^ self error: 'lookupClass is not in my inheritance chain']. + self primitiveFailed! ! + +!Object methodsFor: 'message handling' stamp: 'nk 4/11/2002 14:13'! +perform: selector withEnoughArguments: anArray + "Send the selector, aSymbol, to the receiver with arguments in argArray. + Only use enough arguments for the arity of the selector; supply nils for missing ones." + | numArgs args | + numArgs _ selector numArgs. + anArray size == numArgs + ifTrue: [ ^self perform: selector withArguments: anArray asArray ]. + + args _ Array new: numArgs. + args replaceFrom: 1 + to: (anArray size min: args size) + with: anArray + startingAt: 1. + + ^ self perform: selector withArguments: args! ! + +!Object methodsFor: 'message handling' stamp: 'di 3/26/1999 07:52'! +perform: aSymbol with: anObject + "Send the selector, aSymbol, to the receiver with anObject as its argument. + Fail if the number of arguments expected by the selector is not one. + Primitive. Optional. See Object documentation whatIsAPrimitive." + + + ^ self perform: aSymbol withArguments: (Array with: anObject)! ! + +!Object methodsFor: 'message handling' stamp: 'di 3/26/1999 07:52'! +perform: aSymbol with: firstObject with: secondObject + "Send the selector, aSymbol, to the receiver with the given arguments. + Fail if the number of arguments expected by the selector is not two. + Primitive. Optional. See Object documentation whatIsAPrimitive." + + + ^ self perform: aSymbol withArguments: (Array with: firstObject with: secondObject)! ! + +!Object methodsFor: 'message handling' stamp: 'di 3/26/1999 07:51'! +perform: aSymbol with: firstObject with: secondObject with: thirdObject + "Send the selector, aSymbol, to the receiver with the given arguments. + Fail if the number of arguments expected by the selector is not three. + Primitive. Optional. See Object documentation whatIsAPrimitive." + + + ^ self perform: aSymbol + withArguments: (Array with: firstObject with: secondObject with: thirdObject)! ! + +!Object methodsFor: 'message handling' stamp: 'NS 1/28/2004 11:19'! +withArgs: argArray executeMethod: compiledMethod + "Execute compiledMethod against the receiver and args in argArray" + + | selector | + + selector _ Symbol new. + self class addSelectorSilently: selector withMethod: compiledMethod. + ^ [self perform: selector withArguments: argArray] + ensure: [self class basicRemoveSelector: selector]! ! + +!Object methodsFor: 'message handling' stamp: 'md 1/20/2006 16:28'! +with: arg1 executeMethod: compiledMethod + "Execute compiledMethod against the receiver and arg1" + + "" "uncomment once prim 189 is in VM" + ^ self withArgs: {arg1} executeMethod: compiledMethod! ! + +!Object methodsFor: 'message handling' stamp: 'md 1/20/2006 16:28'! +with: arg1 with: arg2 executeMethod: compiledMethod + "Execute compiledMethod against the receiver and arg1 & arg2" + + "" "uncomment once prim 189 is in VM" + ^ self withArgs: {arg1. arg2} executeMethod: compiledMethod! ! + +!Object methodsFor: 'message handling' stamp: 'md 1/20/2006 16:28'! +with: arg1 with: arg2 with: arg3 executeMethod: compiledMethod + "Execute compiledMethod against the receiver and arg1, arg2, & arg3" + + "" "uncomment once prim 189 is in VM" + ^ self withArgs: {arg1. arg2. arg3} executeMethod: compiledMethod! ! + +!Object methodsFor: 'message handling' stamp: 'md 1/20/2006 16:28'! +with: arg1 with: arg2 with: arg3 with: arg4 executeMethod: compiledMethod + "Execute compiledMethod against the receiver and arg1, arg2, arg3, & arg4" + + "" "uncomment once prim 189 is in VM" + ^ self withArgs: {arg1. arg2. arg3. arg4} executeMethod: compiledMethod! ! + + +!Object methodsFor: 'objects from disk' stamp: 'tk 4/8/1999 12:46'! +comeFullyUpOnReload: smartRefStream + "Normally this read-in object is exactly what we want to store. 7/26/96 tk" + + ^ self! ! + +!Object methodsFor: 'objects from disk' stamp: 'RAA 12/20/2000 16:51'! +convertToCurrentVersion: varDict refStream: smartRefStrm + + "subclasses should implement if they wish to convert old instances to modern ones"! ! + +!Object methodsFor: 'objects from disk' stamp: 'tk 11/29/2004 15:04'! +fixUponLoad: aProject seg: anImageSegment + "change the object due to conventions that have changed on +the project level. (sent to all objects in the incoming project). +Specific classes should reimplement this."! ! + +!Object methodsFor: 'objects from disk' stamp: 'RAA 1/10/2001 14:02'! +indexIfCompact + + ^0 "helps avoid a #respondsTo: in publishing"! ! + +!Object methodsFor: 'objects from disk' stamp: 'tk 2/24/1999 11:08'! +objectForDataStream: refStrm + "Return an object to store on an external data stream." + + ^ self! ! + +!Object methodsFor: 'objects from disk' stamp: 'tk 4/8/1999 12:05'! +readDataFrom: aDataStream size: varsOnDisk + "Fill in the fields of self based on the contents of aDataStream. Return self. + Read in the instance-variables written by Object>>storeDataOn:. + NOTE: This method must send beginReference: before reading any objects from aDataStream that might reference it. + Allow aDataStream to have fewer inst vars. See SmartRefStream." + | cntInstVars cntIndexedVars | + + cntInstVars _ self class instSize. + self class isVariable + ifTrue: [cntIndexedVars _ varsOnDisk - cntInstVars. + cntIndexedVars < 0 ifTrue: [ + self error: 'Class has changed too much. Define a convertxxx method']] + ifFalse: [cntIndexedVars _ 0. + cntInstVars _ varsOnDisk]. "OK if fewer than now" + + aDataStream beginReference: self. + 1 to: cntInstVars do: + [:i | self instVarAt: i put: aDataStream next]. + 1 to: cntIndexedVars do: + [:i | self basicAt: i put: aDataStream next]. + "Total number read MUST be equal to varsOnDisk!!" + ^ self "If we ever return something other than self, fix calls + on (super readDataFrom: aDataStream size: anInteger)"! ! + +!Object methodsFor: 'objects from disk' stamp: 'CdG 10/17/2005 20:32'! +saveOnFile + "Ask the user for a filename and save myself on a SmartReferenceStream file. Writes out the version and class structure. The file is fileIn-able. Does not file out the class of the object. tk 6/26/97 13:48" + + | aFileName fileStream | + aFileName := self class name asFileName. "do better?" + aFileName := UIManager default + request: 'File name?' translated initialAnswer: aFileName. + aFileName size == 0 ifTrue: [^ Beeper beep]. + + fileStream := FileStream newFileNamed: aFileName asFileName. + fileStream fileOutClass: nil andObject: self.! ! + +!Object methodsFor: 'objects from disk' stamp: 'tk 8/9/2001 15:40'! +storeDataOn: aDataStream + "Store myself on a DataStream. Answer self. This is a low-level DataStream/ReferenceStream method. See also objectToStoreOnDataStream. NOTE: This method must send 'aDataStream beginInstance:size:' and then (nextPut:/nextPutWeak:) its subobjects. readDataFrom:size: reads back what we write here." + | cntInstVars cntIndexedVars | + + cntInstVars _ self class instSize. + cntIndexedVars _ self basicSize. + aDataStream + beginInstance: self class + size: cntInstVars + cntIndexedVars. + 1 to: cntInstVars do: + [:i | aDataStream nextPut: (self instVarAt: i)]. + + "Write fields of a variable length object. When writing to a dummy + stream, don't bother to write the bytes" + ((aDataStream byteStream class == DummyStream) and: [self class isBits]) ifFalse: [ + 1 to: cntIndexedVars do: + [:i | aDataStream nextPut: (self basicAt: i)]]. +! ! + + +!Object methodsFor: 'parts bin' stamp: 'sw 10/24/2001 16:34'! +descriptionForPartsBin + "If the receiver is a member of a class that would like to be represented in a parts bin, answer the name by which it should be known, and a documentation string to be provided, for example, as balloon help. When the 'nativitySelector' is sent to the 'globalReceiver', it is expected that some kind of Morph will result. The parameters used in the implementation below are for documentation purposes only!!" + + ^ DescriptionForPartsBin + formalName: 'PutFormalNameHere' + categoryList: #(PutACategoryHere MaybePutAnotherCategoryHere) + documentation: 'Put the balloon help here' + globalReceiverSymbol: #PutAGlobalHere + nativitySelector: #PutASelectorHere! ! + + +!Object methodsFor: 'printing' stamp: 'di 6/20/97 08:57'! +fullPrintString + "Answer a String whose characters are a description of the receiver." + + ^ String streamContents: [:s | self printOn: s]! ! + +!Object methodsFor: 'printing'! +isLiteral + "Answer whether the receiver has a literal text form recognized by the + compiler." + + ^false! ! + +!Object methodsFor: 'printing' stamp: 'sma 6/1/2000 09:28'! +longPrintOn: aStream + "Append to the argument, aStream, the names and values of all + of the receiver's instance variables." + + self class allInstVarNames doWithIndex: + [:title :index | + aStream nextPutAll: title; + nextPut: $:; + space; + tab; + print: (self instVarAt: index); + cr]! ! + +!Object methodsFor: 'printing' stamp: 'tk 10/19/2001 11:18'! +longPrintOn: aStream limitedTo: sizeLimit indent: indent + "Append to the argument, aStream, the names and values of all of the receiver's instance variables. Limit is the length limit for each inst var." + + self class allInstVarNames doWithIndex: + [:title :index | + indent timesRepeat: [aStream tab]. + aStream nextPutAll: title; + nextPut: $:; + space; + tab; + nextPutAll: + ((self instVarAt: index) printStringLimitedTo: (sizeLimit -3 -title size max: 1)); + cr]! ! + +!Object methodsFor: 'printing' stamp: 'tk 10/16/2001 19:41'! +longPrintString + "Answer a String whose characters are a description of the receiver." + + | str | + str _ String streamContents: [:aStream | self longPrintOn: aStream]. + "Objects without inst vars should return something" + ^ str isEmpty ifTrue: [self printString, String cr] ifFalse: [str]! ! + +!Object methodsFor: 'printing' stamp: 'BG 11/7/2004 13:39'! +longPrintStringLimitedTo: aLimitValue + "Answer a String whose characters are a description of the receiver." + + | str | + str _ String streamContents: [:aStream | self longPrintOn: aStream limitedTo: aLimitValue indent: 0]. + "Objects without inst vars should return something" + ^ str isEmpty ifTrue: [self printString, String cr] ifFalse: [str]! ! + +!Object methodsFor: 'printing' stamp: 'sw 3/7/2001 13:14'! +nominallyUnsent: aSelectorSymbol + "From within the body of a method which is not formally sent within the system, but which you intend to have remain in the system (for potential manual invocation, or for documentation, or perhaps because it's sent by commented-out-code that you anticipate uncommenting out someday, send this message, with the selector itself as the argument. + +This will serve two purposes: + + (1) The method will not be returned by searches for unsent selectors (because it, in a manner of speaking, sends itself). + (2) You can locate all such methods by browsing senders of #nominallyUnsent:" + + false ifTrue: [self flag: #nominallyUnsent:] "So that this method itself will appear to be sent" +! ! + +!Object methodsFor: 'printing' stamp: 'sma 6/1/2000 09:31'! +printOn: aStream + "Append to the argument, aStream, a sequence of characters that + identifies the receiver." + + | title | + title _ self class name. + aStream + nextPutAll: (title first isVowel ifTrue: ['an '] ifFalse: ['a ']); + nextPutAll: title! ! + +!Object methodsFor: 'printing' stamp: 'sma 6/1/2000 09:22'! +printString + "Answer a String whose characters are a description of the receiver. + If you want to print without a character limit, use fullPrintString." + + ^ self printStringLimitedTo: 50000! ! + +!Object methodsFor: 'printing' stamp: 'tk 5/7/1999 16:20'! +printStringLimitedTo: limit + "Answer a String whose characters are a description of the receiver. + If you want to print without a character limit, use fullPrintString." + | limitedString | + limitedString _ String streamContents: [:s | self printOn: s] limitedTo: limit. + limitedString size < limit ifTrue: [^ limitedString]. + ^ limitedString , '...etc...'! ! + +!Object methodsFor: 'printing' stamp: 'MPW 1/1/1901 00:30'! +propertyList + "Answer a String whose characters are a property-list description of the receiver." + + ^ PropertyListEncoder process:self. +! ! + +!Object methodsFor: 'printing' stamp: 'sw 10/17/2000 11:16'! +reportableSize + "Answer a string that reports the size of the receiver -- useful for showing in a list view, for example" + + ^ (self basicSize + self class instSize) printString! ! + +!Object methodsFor: 'printing'! +storeOn: aStream + "Append to the argument aStream a sequence of characters that is an + expression whose evaluation creates an object similar to the receiver." + + aStream nextPut: $(. + self class isVariable + ifTrue: [aStream nextPutAll: '(', self class name, ' basicNew: '; + store: self basicSize; + nextPutAll: ') '] + ifFalse: [aStream nextPutAll: self class name, ' basicNew']. + 1 to: self class instSize do: + [:i | + aStream nextPutAll: ' instVarAt: '; + store: i; + nextPutAll: ' put: '; + store: (self instVarAt: i); + nextPut: $;]. + 1 to: self basicSize do: + [:i | + aStream nextPutAll: ' basicAt: '; + store: i; + nextPutAll: ' put: '; + store: (self basicAt: i); + nextPut: $;]. + aStream nextPutAll: ' yourself)' +! ! + +!Object methodsFor: 'printing' stamp: 'di 6/20/97 09:12'! +storeString + "Answer a String representation of the receiver from which the receiver + can be reconstructed." + + ^ String streamContents: [:s | self storeOn: s]! ! + +!Object methodsFor: 'printing' stamp: 'sw 5/2/1998 13:55'! +stringForReadout + ^ self stringRepresentation! ! + +!Object methodsFor: 'printing'! +stringRepresentation + "Answer a string that represents the receiver. For most objects this is simply its printString, but for strings themselves, it's themselves. 6/12/96 sw" + + ^ self printString ! ! + + +!Object methodsFor: 'scripting' stamp: 'ar 3/17/2001 20:11'! +adaptedToWorld: aWorld + "If I refer to a world or a hand, return the corresponding items in the new world." + ^self! ! + +!Object methodsFor: 'scripting' stamp: 'sw 3/10/2000 13:57'! +defaultFloatPrecisionFor: aGetSelector + "Answer a number indicating the default float precision to be used in a numeric readout for which the receiver is the model." + + ^ 1! ! + +!Object methodsFor: 'scripting' stamp: 'RAA 3/9/2001 17:08'! +evaluateUnloggedForSelf: aCodeString + + ^Compiler evaluate: + aCodeString + for: self + logged: false! ! + +!Object methodsFor: 'scripting' stamp: 'yo 12/25/2003 16:43'! +methodInterfacesForCategory: aCategorySymbol inVocabulary: aVocabulary limitClass: aLimitClass + "Return a list of methodInterfaces for the receiver in the given category, given a vocabulary. aCategorySymbol is the inherent category symbol, not necessarily the wording as expressed in the vocabulary." + + | categorySymbol | + categorySymbol _ aCategorySymbol asSymbol. + + (categorySymbol == ScriptingSystem nameForInstanceVariablesCategory) ifTrue: [ + "user-defined instance variables" + ^ self methodInterfacesForInstanceVariablesCategoryIn: aVocabulary]. + (categorySymbol == ScriptingSystem nameForScriptsCategory) ifTrue: [ + "user-defined scripts" + ^ self methodInterfacesForScriptsCategoryIn: aVocabulary]. + "all others" + ^ self usableMethodInterfacesIn: (aVocabulary methodInterfacesInCategory: categorySymbol + forInstance: self + ofClass: self class + limitClass: aLimitClass) +! ! + +!Object methodsFor: 'scripting' stamp: 'sw 8/3/2001 13:54'! +methodInterfacesForInstanceVariablesCategoryIn: aVocabulary + "Return a collection of methodInterfaces for the instance-variables category. The vocabulary parameter, at present anyway, is not used. And for non-players, the method is at present vacuous in any case" + + ^ OrderedCollection new! ! + +!Object methodsFor: 'scripting' stamp: 'sw 8/3/2001 13:53'! +methodInterfacesForScriptsCategoryIn: aVocabulary + "Answer a list of method interfaces for the category #scripts, as seen in a viewer or other tool. The vocabulary argument is not presently used. Also, at present, only Players really do anyting interesting here." + + ^ OrderedCollection new! ! + +!Object methodsFor: 'scripting' stamp: 'RAA 2/16/2001 19:37'! +selfWrittenAsIll + + ^self! ! + +!Object methodsFor: 'scripting' stamp: 'RAA 2/16/2001 19:38'! +selfWrittenAsIm + + ^self! ! + +!Object methodsFor: 'scripting' stamp: 'RAA 2/16/2001 19:37'! +selfWrittenAsMe + + ^self! ! + +!Object methodsFor: 'scripting' stamp: 'RAA 2/16/2001 19:37'! +selfWrittenAsMy + + ^self! ! + +!Object methodsFor: 'scripting' stamp: 'RAA 2/16/2001 19:38'! +selfWrittenAsThis + + ^self! ! + + +!Object methodsFor: 'scripts-kernel' stamp: 'nk 10/14/2004 10:55'! +universalTilesForGetterOf: aMethodInterface + "Return universal tiles for a getter on the given method interface." + + | ms argTile argArray itsSelector | + itsSelector _ aMethodInterface selector. + argArray _ #(). + + "Four gratuituous special cases..." + + (itsSelector == #color:sees:) ifTrue: + [argTile _ ScriptingSystem tileForArgType: #Color. + argArray _ Array with: argTile colorSwatch color with: argTile colorSwatch color copy]. + + itsSelector == #seesColor: ifTrue: + [argTile _ ScriptingSystem tileForArgType: #Color. + argArray _ Array with: argTile colorSwatch color]. + + (#(touchesA: overlaps: overlapsAny:) includes: itsSelector) ifTrue: + [argTile _ ScriptingSystem tileForArgType: #Player. + argArray _ Array with: argTile actualObject]. + + ms _ MessageSend receiver: self selector: itsSelector arguments: argArray. + ^ ms asTilesIn: self class globalNames: (self class officialClass ~~ CardPlayer) + "For CardPlayers, use 'self'. For others, name it, and use its name."! ! + +!Object methodsFor: 'scripts-kernel' stamp: 'tk 9/28/2001 13:30'! +universalTilesForInterface: aMethodInterface + "Return universal tiles for the given method interface. Record who self is." + + | ms argTile itsSelector aType argList | + itsSelector _ aMethodInterface selector. + argList _ OrderedCollection new. + aMethodInterface argumentVariables doWithIndex: + [:anArgumentVariable :anIndex | + argTile _ ScriptingSystem tileForArgType: (aType _ aMethodInterface typeForArgumentNumber: anIndex). + argList add: (aType == #Player + ifTrue: [argTile actualObject] + ifFalse: [argTile literal]). "default value for each type"]. + + ms _ MessageSend receiver: self selector: itsSelector arguments: argList asArray. + ^ ms asTilesIn: self class globalNames: (self class officialClass ~~ CardPlayer) + "For CardPlayers, use 'self'. For others, name it, and use its name."! ! + + +!Object methodsFor: 'self evaluating' stamp: 'sd 7/31/2005 21:47'! +isSelfEvaluating + ^ self isLiteral! ! + + +!Object methodsFor: 'system primitives'! +asOop + "Primitive. Answer a SmallInteger whose value is half of the receiver's + object pointer (interpreting object pointers as 16-bit signed quantities). + Fail if the receiver is a SmallInteger. Essential. See Object documentation + whatIsAPrimitive." + + + self primitiveFailed! ! + +!Object methodsFor: 'system primitives' stamp: 'di 1/9/1999 15:19'! +becomeForward: otherObject + "Primitive. All variables in the entire system that used to point + to the receiver now point to the argument. + Fails if either argument is a SmallInteger." + + (Array with: self) + elementsForwardIdentityTo: + (Array with: otherObject)! ! + +!Object methodsFor: 'system primitives' stamp: 'zz 3/3/2004 23:53'! +becomeForward: otherObject copyHash: copyHash + "Primitive. All variables in the entire system that used to point to the receiver now point to the argument. + If copyHash is true, the argument's identity hash bits will be set to those of the receiver. + Fails if either argument is a SmallInteger." + + (Array with: self) + elementsForwardIdentityTo: + (Array with: otherObject) + copyHash: copyHash! ! + +!Object methodsFor: 'system primitives' stamp: 'sw 10/16/2000 10:59'! +className + "Answer a string characterizing the receiver's class, for use in list views for example" + + ^ self class name asString! ! + +!Object methodsFor: 'system primitives' stamp: 'sw 10/16/2000 11:04'! +creationStamp + "Answer a string which reports the creation particulars of the receiver. Intended perhaps for list views, but this is presently a feature not easily accessible" + + ^ ''! ! + +!Object methodsFor: 'system primitives'! +instVarAt: index + "Primitive. Answer a fixed variable in an object. The numbering of the + variables corresponds to the named instance variables. Fail if the index + is not an Integer or is not the index of a fixed variable. Essential. See + Object documentation whatIsAPrimitive." + + + "Access beyond fixed variables." + ^self basicAt: index - self class instSize ! ! + +!Object methodsFor: 'system primitives'! +instVarAt: anInteger put: anObject + "Primitive. Store a value into a fixed variable in the receiver. The + numbering of the variables corresponds to the named instance variables. + Fail if the index is not an Integer or is not the index of a fixed variable. + Answer the value stored as the result. Using this message violates the + principle that each object has sovereign control over the storing of + values into its instance variables. Essential. See Object documentation + whatIsAPrimitive." + + + "Access beyond fixed fields" + ^self basicAt: anInteger - self class instSize put: anObject! ! + +!Object methodsFor: 'system primitives' stamp: 'sw 10/16/2000 11:09'! +instVarNamed: aString + "Return the value of the instance variable in me with that name. Slow and unclean, but very useful. " + + ^ self instVarAt: (self class allInstVarNames indexOf: aString asString) + + +! ! + +!Object methodsFor: 'system primitives' stamp: 'sw 10/16/2000 11:10'! +instVarNamed: aString put: aValue + "Store into the value of the instance variable in me of that name. Slow and unclean, but very useful. " + + ^ self instVarAt: (self class allInstVarNames indexOf: aString asString) put: aValue +! ! + +!Object methodsFor: 'system primitives' stamp: 'sw 10/17/2000 11:12'! +oopString + "Answer a string that represents the oop of the receiver" + + ^ self asOop printString! ! + +!Object methodsFor: 'system primitives' stamp: 'ar 3/2/2001 01:34'! +primitiveChangeClassTo: anObject + "Primitive. Change the class of the receiver into the class of the argument given that the format of the receiver matches the format of the argument's class. Fail if receiver or argument are SmallIntegers, or the receiver is an instance of a compact class and the argument isn't, or when the argument's class is compact and the receiver isn't, or when the format of the receiver is different from the format of the argument's class, or when the arguments class is fixed and the receiver's size differs from the size that an instance of the argument's class should have. + Note: The primitive will fail in most cases that you think might work. This is mostly because of a) the difference between compact and non-compact classes, and b) because of differences in the format. As an example, '(Array new: 3) primitiveChangeClassTo: Morph basicNew' would fail for three of the reasons mentioned above. Array is compact, Morph is not (failure #1). Array is variable and Morph is fixed (different format - failure #2). Morph is a fixed-field-only object and the array is too short (failure #3). + The facility is really provided for certain, very specific applications (mostly related to classes changing shape) and not for casual use." + + + self primitiveFailed! ! + +!Object methodsFor: 'system primitives' stamp: 'di 3/27/1999 12:21'! +rootStubInImageSegment: imageSegment + + ^ ImageSegmentRootStub new + xxSuperclass: nil + format: nil + segment: imageSegment! ! + +!Object methodsFor: 'system primitives'! +someObject + "Primitive. Answer the first object in the enumeration of all + objects." + + + self primitiveFailed.! ! + + +!Object methodsFor: 'testing' stamp: 'sw 9/26/2001 11:58'! +basicType + "Answer a symbol representing the inherent type of the receiver" + + ^ #Object! ! + +!Object methodsFor: 'testing' stamp: 'sw 5/3/2001 16:19'! +beViewed + "Open up a viewer on the receiver. The Presenter is invited to decide just how to present this viewer" + + self uniqueNameForReference. "So the viewer will have something nice to refer to" + self presenter viewObject: self! ! + +!Object methodsFor: 'testing' stamp: 'sw 10/16/2000 11:01'! +costumes + "Answer a list of costumes associated with the receiver. The appearance of this method in class Object serves only as a backstop, probably only transitionally" + + ^ nil! ! + +!Object methodsFor: 'testing' stamp: 'sw 1/12/98 18:09'! +haltIfNil! ! + +!Object methodsFor: 'testing' stamp: 'md 1/20/2006 17:09'! +hasLiteralSuchThat: testBlock + "This is the end of the imbedded structure path so return false." + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'md 1/20/2006 17:10'! +hasLiteralThorough: literal + "Answer true if literal is identical to any literal in this array, even if imbedded in further structures. This is the end of the imbedded structure path so return false." + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'sw 1/30/2001 22:24'! +haveFullProtocolBrowsed + "Open up a Lexicon on the receiver" + + ^ self haveFullProtocolBrowsedShowingSelector: nil + + "(2@3) haveFullProtocolBrowsed" +! ! + +!Object methodsFor: 'testing' stamp: 'ar 9/27/2005 21:04'! +haveFullProtocolBrowsedShowingSelector: aSelector + "Open up a Lexicon on the receiver, having it open up showing aSelector, which may be nil" + + | aBrowser | + aBrowser := (Smalltalk at: #InstanceBrowser ifAbsent:[^nil]) new useVocabulary: Vocabulary fullVocabulary. + aBrowser openOnObject: self inWorld: ActiveWorld showingSelector: aSelector + + "(2@3) haveFullProtocolBrowsed"! ! + +!Object methodsFor: 'testing' stamp: 'md 7/30/2005 21:21'! +isArray + ^false! ! + +!Object methodsFor: 'testing' stamp: 'ar 7/9/1999 18:18'! +isBehavior + "Return true if the receiver is a behavior. + Note: Do not override in any class except behavior." + ^false! ! + +!Object methodsFor: 'testing' stamp: 'ajh 1/21/2003 13:15'! +isBlock + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'md 11/21/2003 12:14'! +isBlockClosure + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'yo 8/28/2002 13:41'! +isCharacter + + ^ false. +! ! + +!Object methodsFor: 'testing' stamp: 'ar 8/17/1999 19:43'! +isCollection + "Return true if the receiver is some sort of Collection and responds to basic collection messages such as #size and #do:" + ^false! ! + +!Object methodsFor: 'testing'! +isColor + "Answer true if receiver is a Color. False by default." + + ^ false +! ! + +!Object methodsFor: 'testing' stamp: 'nk 4/17/2004 19:43'! +isColorForm + ^false! ! + +!Object methodsFor: 'testing' stamp: 'md 11/21/2003 12:14'! +isCompiledMethod + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'mk 10/27/2003 17:33'! +isComplex + "Answer true if receiver is a Complex number. False by default." + + ^ false +! ! + +!Object methodsFor: 'testing' stamp: 'md 8/11/2005 16:45'! +isDictionary + ^false! ! + +!Object methodsFor: 'testing' stamp: 'di 11/9/1998 09:38'! +isFloat + "Overridden to return true in Float, natch" + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'ar 10/30/2000 23:22'! +isForm + ^false! ! + +!Object methodsFor: 'testing' stamp: 'len 1/13/98 21:18'! +isFraction + "Answer true if the receiver is a Fraction." + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'rhi 8/14/2003 08:51'! +isHeap + + ^ false! ! + +!Object methodsFor: 'testing'! +isInteger + "Overridden to return true in Integer." + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'rhi 8/12/2003 09:52'! +isInterval + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'nk 4/25/2002 08:04'! +isMessageSend + ^false +! ! + +!Object methodsFor: 'testing' stamp: 'md 2/19/2006 11:24'! +isMethodProperties + ^false! ! + +!Object methodsFor: 'testing'! +isMorph + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'ar 9/13/2000 15:37'! +isMorphicEvent + ^false! ! + +!Object methodsFor: 'testing' stamp: 'gm 2/22/2003 12:56'! +isMorphicModel + "Return true if the receiver is a morphic model" + ^false +! ! + +!Object methodsFor: 'testing'! +isNumber + "Overridden to return true in Number, natch" + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'di 11/6/1998 08:04'! +isPoint + "Overridden to return true in Point." + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'ikp 9/26/97 14:45'! +isPseudoContext + ^false! ! + +!Object methodsFor: 'testing' stamp: 'md 10/2/2005 21:52'! +isRectangle + ^false! ! + +!Object methodsFor: 'testing' stamp: 'nk 6/14/2004 16:49'! +isSketchMorph + ^false! ! + +!Object methodsFor: 'testing' stamp: 'ar 12/23/1999 15:43'! +isStream + "Return true if the receiver responds to the stream protocol" + ^false +! ! + +!Object methodsFor: 'testing' stamp: 'sma 6/15/2000 15:48'! +isString + "Overridden to return true in String, natch" + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'md 4/30/2003 15:30'! +isSymbol + ^ false ! ! + +!Object methodsFor: 'testing' stamp: 'jam 3/9/2003 15:10'! +isSystemWindow +"answer whatever the receiver is a SystemWindow" + ^ false! ! + +!Object methodsFor: 'testing'! +isText + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'pmm 7/6/2006 20:46'! +isTrait + "Return true if the receiver is a trait. + Note: Do not override in any class except TraitBehavior." + ^false! ! + +!Object methodsFor: 'testing' stamp: 'tk 10/21/97 12:45'! +isTransparent + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'ar 8/14/2001 23:19'! +isVariableBinding + "Return true if I represent a literal variable binding" + ^false + ! ! + +!Object methodsFor: 'testing' stamp: 'ls 7/14/1998 21:45'! +isWebBrowser + "whether this object is a web browser. See class: Scamper" + ^false! ! + +!Object methodsFor: 'testing' stamp: 'sw 10/27/2000 06:58'! +knownName + "If a formal name has been handed out for this object, answer it, else nil" + + ^ Preferences capitalizedReferences + ifTrue: + [References keyAtValue: self ifAbsent: [nil]] + ifFalse: + [nil]! ! + +!Object methodsFor: 'testing' stamp: 'sw 9/27/96'! +name + "Answer a name for the receiver. This is used generically in the title of certain inspectors, such as the referred-to inspector, and specificially by various subsystems. By default, we let the object just print itself out.. " + + ^ self printString! ! + +!Object methodsFor: 'testing' stamp: 'sw 11/19/2001 13:28'! +nameForViewer + "Answer a name to be shown in a Viewer that is viewing the receiver" + + | aName | + (aName _ self uniqueNameForReferenceOrNil) ifNotNil: [^ aName]. + (aName _ self knownName) ifNotNil: [^ aName]. + + ^ [(self asString copyWithout: Character cr) truncateTo: 27] ifError: + [:msg :rcvr | ^ self class name printString]! ! + +!Object methodsFor: 'testing'! +notNil + "Coerces nil to false and everything else to true." + + ^true! ! + +!Object methodsFor: 'testing' stamp: 'tk 9/6/2001 19:15'! +openInstanceBrowserWithTiles + "Open up an instance browser on me with tiles as the code type, and with the search level as desired." + + | aBrowser | + aBrowser _ InstanceBrowser new. + aBrowser useVocabulary: Vocabulary fullVocabulary. + aBrowser limitClass: self class. + aBrowser contentsSymbol: #tiles. "preset it to make extra buttons (tile menus)" + aBrowser openOnObject: self inWorld: ActiveWorld showingSelector: nil. + aBrowser contentsSymbol: #source. + aBrowser toggleShowingTiles. + + " +(2@3) openInstanceBrowserWithTiles. +WatchMorph new openInstanceBrowserWithTiles +"! ! + +!Object methodsFor: 'testing' stamp: 'tk 7/28/2005 04:50'! +renameInternal: newName + "Change the internal name (because of a conflict) but leave the external name unchanged. Change Player class name, but do not change the names that appear in tiles. Any object that might be pointed to in the References dictionary might get this message sent to it upon reload" + + ^ nil "caller will renameTo:. new name may be different"! ! + +!Object methodsFor: 'testing' stamp: 'sw 2/27/2002 14:55'! +renameTo: newName + "If the receiver has an inherent idea about its own name, it should take action here. Any object that might be pointed to in the References dictionary might get this message sent to it upon reload"! ! + +!Object methodsFor: 'testing' stamp: 'sw 1/18/2001 13:43'! +showDiffs + "Answer whether the receiver, serving as the model of a text-bearing entity, is 'showing differences' -- if it is, the editor may wish to show special feedback" + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'sw 10/20/1999 14:52'! +stepAt: millisecondClockValue in: aWindow + + ^ self stepIn: aWindow! ! + +!Object methodsFor: 'testing' stamp: 'sw 10/19/1999 08:16'! +stepIn: aWindow + + ^ self step! ! + +!Object methodsFor: 'testing' stamp: 'sw 10/19/1999 08:21'! +stepTime + + ^ 1000 "milliseconds -- default backstop for objects serving as models of system windows"! ! + +!Object methodsFor: 'testing' stamp: 'sw 10/19/1999 08:22'! +stepTimeIn: aSystemWindow + + ^ 1000 "milliseconds -- default backstop for objects serving as models of system windows"! ! + +!Object methodsFor: 'testing' stamp: 'sw 5/3/2001 18:22'! +vocabularyDemanded + "Answer a vocabulary that the receiver insists be used when it is looked at in a Viewer. This allows specific classes to insist on specific custom vocabularies" + + ^ nil! ! + +!Object methodsFor: 'testing' stamp: 'sw 11/13/2001 07:26'! +wantsDiffFeedback + "Answer whether the receiver, serving as the model of a text-bearing entity, would like for 'diffs' green pane-border feedback to be shown" + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'di 1/8/1999 15:04'! +wantsSteps + "Overridden by morphic classes whose instances want to be stepped, + or by model classes who want their morphic views to be stepped." + + ^ false! ! + +!Object methodsFor: 'testing' stamp: 'sw 10/19/1999 08:26'! +wantsStepsIn: aSystemWindow + + ^ self wantsSteps! ! + + +!Object methodsFor: 'thumbnail' stamp: 'dgd 9/25/2004 23:17'! +iconOrThumbnailOfSize: aNumberOrPoint + "Answer an appropiate form to represent the receiver" + ^ nil! ! + + +!Object methodsFor: 'translation support'! +inline: inlineFlag + "For translation only; noop when running in Smalltalk."! ! + +!Object methodsFor: 'translation support'! +var: varSymbol declareC: declString + "For translation only; noop when running in Smalltalk."! ! + + +!Object methodsFor: 'undo' stamp: 'di 9/11/2000 20:32'! +capturedState + "May be overridden in subclasses." + + ^ self shallowCopy +! ! + +!Object methodsFor: 'undo' stamp: 'di 9/11/2000 20:29'! +commandHistory + "Return the command history for the receiver" + | w | + (w _ self currentWorld) ifNotNil: [^ w commandHistory]. + ^ CommandHistory new. "won't really record anything but prevent breaking things"! ! + +!Object methodsFor: 'undo' stamp: 'di 12/12/2000 15:01'! +purgeAllCommands + "Purge all commands for this object" + Preferences useUndo ifFalse: [^ self]. "get out quickly" + self commandHistory purgeAllCommandsSuchThat: [:cmd | cmd undoTarget == self]. +! ! + +!Object methodsFor: 'undo' stamp: 'di 9/12/2000 08:15'! +redoFromCapturedState: st + "May be overridden in subclasses. See also capturedState" + + self undoFromCapturedState: st "Simple cases are symmetric" +! ! + +!Object methodsFor: 'undo' stamp: 'sw 11/16/2000 14:42'! +refineRedoTarget: target selector: aSymbol arguments: arguments in: refineBlock + "Any object can override this method to refine its redo specification" + + ^ refineBlock + value: target + value: aSymbol + value: arguments! ! + +!Object methodsFor: 'undo' stamp: 'sw 11/16/2000 14:42'! +refineUndoTarget: target selector: aSymbol arguments: arguments in: refineBlock + "Any object can override this method to refine its undo specification" + + ^ refineBlock + value: target + value: aSymbol + value: arguments! ! + +!Object methodsFor: 'undo' stamp: 'di 9/11/2000 20:30'! +rememberCommand: aCommand + "Remember the given command for undo" + Preferences useUndo ifFalse: [^ self]. "get out quickly" + ^ self commandHistory rememberCommand: aCommand! ! + +!Object methodsFor: 'undo' stamp: 'di 9/11/2000 20:30'! +rememberUndoableAction: actionBlock named: caption + | cmd result | + cmd _ Command new cmdWording: caption. + cmd undoTarget: self selector: #undoFromCapturedState: argument: self capturedState. + result _ actionBlock value. + cmd redoTarget: self selector: #redoFromCapturedState: argument: self capturedState. + self rememberCommand: cmd. + ^ result! ! + +!Object methodsFor: 'undo' stamp: 'di 9/11/2000 20:32'! +undoFromCapturedState: st + "May be overridden in subclasses. See also capturedState" + + self copyFrom: st +! ! + + +!Object methodsFor: 'updating'! +changed + "Receiver changed in a general way; inform all the dependents by + sending each dependent an update: message." + + self changed: self! ! + +!Object methodsFor: 'updating'! +changed: aParameter + "Receiver changed. The change is denoted by the argument aParameter. + Usually the argument is a Symbol that is part of the dependent's change + protocol. Inform all of the dependents." + + self dependents do: [:aDependent | aDependent update: aParameter]! ! + +!Object methodsFor: 'updating' stamp: 'nk 2/17/2004 11:12'! +changed: anAspect with: anObject + "Receiver changed. The change is denoted by the argument anAspect. + Usually the argument is a Symbol that is part of the dependent's change + protocol. Inform all of the dependents. Also pass anObject for additional information." + + self dependents do: [:aDependent | aDependent update: anAspect with: anObject]! ! + +!Object methodsFor: 'updating' stamp: 'sw 10/12/1999 18:15'! +handledListVerification + "When a self-updating PluggableListMorph lazily checks to see the state of affairs, it first gives its model an opportunity to handle the list verification itself (this is appropriate for some models, such as VersionsBrowser); if a list's model has indeed handled things itself, it returns true here" + + ^ false! ! + +!Object methodsFor: 'updating' stamp: 'sw 10/31/1999 00:15'! +noteSelectionIndex: anInteger for: aSymbol + "backstop"! ! + +!Object methodsFor: 'updating'! +okToChange + "Allows a controller to ask this of any model" + ^ true! ! + +!Object methodsFor: 'updating' stamp: 'sw 10/19/1999 14:39'! +updateListsAndCodeIn: aWindow + self canDiscardEdits ifFalse: [^ self]. + aWindow updatablePanes do: [:aPane | aPane verifyContents]! ! + +!Object methodsFor: 'updating' stamp: 'sma 2/29/2000 20:05'! +update: aParameter + "Receive a change notice from an object of whom the receiver is a + dependent. The default behavior is to do nothing; a subclass might want + to change itself in some way." + + ^ self! ! + +!Object methodsFor: 'updating' stamp: 'nk 2/17/2004 11:13'! +update: anAspect with: anObject + "Receive a change notice from an object of whom the receiver is a + dependent. The default behavior is to call update:, + which by default does nothing; a subclass might want + to change itself in some way." + + ^ self update: anAspect! ! + +!Object methodsFor: 'updating' stamp: 'jm 8/20/1998 18:26'! +windowIsClosing + "This message is used to inform a models that its window is closing. Most models do nothing, but some, such as the Debugger, must do some cleanup. Note that this mechanism must be used with care by models that support multiple views, since one view may be closed while others left open." +! ! + + +!Object methodsFor: 'user interface' stamp: 'sw 10/4/1999 08:13'! +addModelItemsToWindowMenu: aMenu + "aMenu is being constructed to be presented to the user in response to the user's pressing on the menu widget in the title bar of a morphic window. Here, the model is given the opportunity to add any model-specific items to the menu, whose default target is the SystemWindow itself."! ! + +!Object methodsFor: 'user interface' stamp: 'sw 10/5/1998 14:39'! +addModelMenuItemsTo: aCustomMenu forMorph: aMorph hand: aHandMorph + "The receiver serves as the model for aMorph; a menu is being constructed for the morph, and here the receiver is able to add its own items" +! ! + +!Object methodsFor: 'user interface' stamp: 'sma 11/12/2000 11:43'! +asExplorerString + ^ self printString! ! + +!Object methodsFor: 'user interface' stamp: 'sw 7/13/1999 15:53'! +defaultBackgroundColor + "Answer the color to be used as the base window color for a window whose model is an object of the receiver's class" + + ^ Preferences windowColorFor: self class name! ! + +!Object methodsFor: 'user interface'! +defaultLabelForInspector + "Answer the default label to be used for an Inspector window on the receiver." + + ^ self class name! ! + +!Object methodsFor: 'user interface' stamp: 'RAA 7/10/2000 08:11'! +eToyStreamedRepresentationNotifying: aWidget + + | outData | + [ outData _ SmartRefStream streamedRepresentationOf: self ] + on: ProgressInitiationException + do: [ :ex | + ex sendNotificationsTo: [ :min :max :curr | + aWidget ifNotNil: [aWidget flashIndicator: #working]. + ]. + ]. + ^outData +! ! + +!Object methodsFor: 'user interface' stamp: 'ar 9/27/2005 20:29'! +explore + ^ToolSet explore: self! ! + +!Object methodsFor: 'user interface' stamp: 'sw 8/15/97 17:25'! +fullScreenSize + "Answer the size to which a window displaying the receiver should be set" + | adj | + adj _ (3 * Preferences scrollBarWidth) @ 0. + ^ Rectangle origin: adj extent: (DisplayScreen actualScreenSize - adj)! ! + +!Object methodsFor: 'user interface' stamp: 'RAA 6/21/1999 11:27'! +hasContentsInExplorer + + ^self basicSize > 0 or: [self class allInstVarNames isEmpty not] +! ! + +!Object methodsFor: 'user interface' stamp: 'rbb 3/1/2005 09:28'! +inform: aString + "Display a message for the user to read and then dismiss. 6/9/96 sw" + + aString isEmptyOrNil ifFalse: [UIManager default inform: aString]! ! + +!Object methodsFor: 'user interface'! +initialExtent + "Answer the desired extent for the receiver when a view on it is first opened on the screen. + 5/22/96 sw: in the absence of any override, obtain from RealEstateAgent" + + ^ RealEstateAgent standardWindowExtent! ! + +!Object methodsFor: 'user interface' stamp: 'ar 9/27/2005 20:30'! +inspectWithLabel: aLabel + "Create and schedule an Inspector in which the user can examine the receiver's variables." + ^ToolSet inspect: self label: aLabel! ! + +!Object methodsFor: 'user interface' stamp: 'sw 6/12/2001 11:09'! +launchPartVia: aSelector + "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins" + + | aMorph | + aMorph _ self perform: aSelector. + aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. + aMorph openInHand! ! + +!Object methodsFor: 'user interface' stamp: 'sw 6/17/2004 01:47'! +launchPartVia: aSelector label: aString + "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins" + + | aMorph | + aMorph _ self perform: aSelector. + aMorph setNameTo: (ActiveWorld unusedMorphNameLike: aString). + aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. + aMorph openInHand! ! + +!Object methodsFor: 'user interface' stamp: 'sw 10/16/2000 11:11'! +launchTileToRefer + "Create a tile to reference the receiver, and attach it to the hand" + + self currentHand attachMorph: self tileToRefer! ! + +!Object methodsFor: 'user interface' stamp: 'di 5/11/1999 22:26'! +modelSleep + "A window with me as model is being exited or collapsed or closed. + Default response is no-op" ! ! + +!Object methodsFor: 'user interface' stamp: 'di 5/11/1999 22:01'! +modelWakeUp + "A window with me as model is being entered or expanded. Default response is no-op" ! ! + +!Object methodsFor: 'user interface' stamp: 'sw 10/16/1999 22:45'! +modelWakeUpIn: aWindow + "A window with me as model is being entered or expanded. Default response is no-op" + self modelWakeUp! ! + +!Object methodsFor: 'user interface' stamp: 'sw 3/8/1999 15:27'! +mouseUpBalk: evt + "A button I own got a mouseDown, but the user moved out before letting up. Certain kinds of objects (so-called 'radio buttons', for example, and other structures that must always have some selection, e.g. PaintBoxMorph) wish to take special action in this case; this default does nothing." +! ! + +!Object methodsFor: 'user interface' stamp: 'sw 8/22/97 13:14'! +newTileMorphRepresentative + ^ TileMorph new setLiteral: self! ! + +!Object methodsFor: 'user interface' stamp: 'jcg 11/1/2001 13:13'! +notYetImplemented + self inform: 'Not yet implemented (', thisContext sender printString, ')'! ! + +!Object methodsFor: 'user interface' stamp: 'di 6/10/1998 15:06'! +windowReqNewLabel: labelString + "My window's title has been edited. + Return true if this is OK, and override for further behavior." + + ^ true! ! + + +!Object methodsFor: 'viewer' stamp: 'sw 10/16/2000 10:35'! +assureUniClass + "If the receiver is not yet an instance of a uniclass, create a uniclass for it and make the receiver become an instance of that class." + + | anInstance | + self belongsToUniClass ifTrue: [^ self]. + anInstance _ self class instanceOfUniqueClass. + self become: (self as: anInstance class). + ^ anInstance! ! + +!Object methodsFor: 'viewer' stamp: 'sw 10/16/2000 10:41'! +belongsToUniClass + "Answer whether the receiver belongs to a uniclass. For the moment (this is not entirely satisfactory) this is precisely equated with the classname ending in a digit" + + ^ self class name endsWithDigit! ! + +!Object methodsFor: 'viewer' stamp: 'sw 12/11/2000 15:37'! +browseOwnClassSubProtocol + "Open up a ProtocolBrowser on the subprotocol of the receiver" + + ProtocolBrowser openSubProtocolForClass: self class +! ! + +!Object methodsFor: 'viewer' stamp: 'sw 8/4/2001 00:51'! +categoriesForViewer: aViewer + "Answer a list of categories to offer in the given viewer" + + ^ aViewer currentVocabulary categoryListForInstance: self ofClass: self class limitClass: aViewer limitClass! ! + +!Object methodsFor: 'viewer' stamp: 'sw 8/3/2001 22:08'! +categoriesForVocabulary: aVocabulary limitClass: aLimitClass + "Answer a list of categories of methods for the receiver when using the given vocabulary, given that one considers only methods that are implemented not further away than aLimitClass" + + ^ aVocabulary categoryListForInstance: self ofClass: self class limitClass: aLimitClass! ! + +!Object methodsFor: 'viewer' stamp: 'sw 10/25/2000 07:20'! +chooseNewNameForReference + "Offer an opportunity for the receiver, presumed already to be known in the References registry, to be renamed" + + | nameSym current newName | + current _ References keyAtValue: self ifAbsent: [^ self error: 'not found in References']. + + newName _ FillInTheBlank request: 'Please enter new name' initialAnswer: current. + "Want to user some better way of determining the validity of the chosen identifier, and also want to give more precise diagnostic if the string the user types in is not acceptable. Work to be done here." + + newName isEmpty ifTrue: [^ nil]. + ((Scanner isLiteralSymbol: newName) and: [(newName includes: $:) not]) + ifTrue: + [nameSym _ newName capitalized asSymbol. + (((References includesKey: nameSym) not and: + [(Smalltalk includesKey: nameSym) not]) and: + [(ScriptingSystem allKnownClassVariableNames includes: nameSym) not]) + ifTrue: + [(References associationAt: current) key: nameSym. + References rehash. + ^ nameSym]]. + self inform: 'Sorry, that name is not available.'. + ^ nil! ! + +!Object methodsFor: 'viewer' stamp: 'sw 8/3/2001 21:22'! +defaultLimitClassForVocabulary: aVocabulary + "Answer the class to use, by default, as the limit class on a protocol browser or viewer opened up on the receiver, within the purview of the Vocabulary provided" + + ^ (aVocabulary isKindOf: FullVocabulary) + ifTrue: + [self class superclass == Object + ifTrue: + [self class] + ifFalse: + [self class superclass]] + ifFalse: + [ProtoObject]! ! + +!Object methodsFor: 'viewer' stamp: 'sw 2/14/2000 14:24'! +defaultNameStemForInstances + "Answer a basis for names of default instances of the receiver. The default is to let the class specify, but certain instances will want to override. (PasteUpMorphs serving as Worlds come to mind" + + ^ self class defaultNameStemForInstances! ! + +!Object methodsFor: 'viewer' stamp: 'sw 5/22/2001 16:53'! +elementTypeFor: aStringOrSymbol vocabulary: aVocabulary + "Answer a symbol characterizing what kind of element aStringOrSymbol represents. Realistically, at present, this always just returns #systemScript; a prototyped but not-incorporated architecture supported use of a leading colon to characterize an inst var of a system class, and for the moment we still see its remnant here." + + self flag: #deferred. "a loose end in the non-player case" + ^ #systemScript! ! + +!Object methodsFor: 'viewer' stamp: 'sw 5/4/2001 07:04'! +externalName + "Answer an external name by which the receiver is known. Generic implementation here is a transitional backstop. probably" + + ^ self nameForViewer! ! + +!Object methodsFor: 'viewer' stamp: 'sw 5/4/2001 07:06'! +graphicForViewerTab + "When a Viewer is open on the receiver, its tab needs some graphic to show to the user. Answer a form or a morph to serve that purpose. A generic image is used for arbitrary objects, but note my reimplementors" + + ^ ScriptingSystem formAtKey: 'Image'! ! + +!Object methodsFor: 'viewer' stamp: 'sw 5/4/2001 07:08'! +hasUserDefinedSlots + "Answer whether the receiver has any user-defined slots, in the omniuser sense of the term. This is needed to allow Viewers to look at any object, not just at Players." + + ^ false! ! + +!Object methodsFor: 'viewer' stamp: 'sw 8/22/2002 14:07'! +infoFor: anElement inViewer: aViewer + "The user made a gesture asking for info/menu relating to me. Some of the messages dispatched here are not yet available in this image" + + | aMenu elementType | + elementType _ self elementTypeFor: anElement vocabulary: aViewer currentVocabulary. + ((elementType = #systemSlot) | (elementType == #userSlot)) + ifTrue: [^ self slotInfoButtonHitFor: anElement inViewer: aViewer]. + self flag: #deferred. "Use a traditional MenuMorph, and reinstate the pacify thing" + aMenu _ MenuMorph new defaultTarget: aViewer. + #( ('implementors' browseImplementorsOf:) + ('senders' browseSendersOf:) + ('versions' browseVersionsOf:) + - + ('browse full' browseMethodFull:) + ('inheritance' browseMethodInheritance:) + - + ('about this method' aboutMethod:)) do: + + [:pair | + pair = '-' + ifTrue: + [aMenu addLine] + ifFalse: + [aMenu add: pair first target: aViewer selector: pair second argument: anElement]]. + aMenu addLine. + aMenu defaultTarget: self. + #( ('destroy script' removeScript:) + ('rename script' renameScript:) + ('pacify script' pacifyScript:)) do: + [:pair | + aMenu add: pair first target: self selector: pair second argument: anElement]. + + aMenu addLine. + aMenu add: 'show categories....' target: aViewer selector: #showCategoriesFor: argument: anElement. + aMenu items size == 0 ifTrue: "won't happen at the moment a/c the above" + [aMenu add: 'ok' action: nil]. "in case it was a slot -- weird, transitional" + + aMenu addTitle: anElement asString, ' (', elementType, ')'. + + aMenu popUpInWorld: self currentWorld. + ! ! + +!Object methodsFor: 'viewer' stamp: 'sw 9/26/2001 11:58'! +initialTypeForSlotNamed: aName + "Answer the initial type to be ascribed to the given instance variable" + + ^ #Object! ! + +!Object methodsFor: 'viewer' stamp: 'ar 5/26/2001 16:13'! +isPlayerLike + "Return true if the receiver is a player-like object" + ^false! ! + +!Object methodsFor: 'viewer' stamp: 'nk 9/11/2004 16:53'! +methodInterfacesInPresentationOrderFrom: interfaceList forCategory: aCategory + "Answer the interface list sorted in desired presentation order, using a + static master-ordering list, q.v. The category parameter allows an + escape in case one wants to apply different order strategies in different + categories, but for now a single master-priority-ordering is used -- see + the comment in method EToyVocabulary.masterOrderingOfPhraseSymbols" + + | masterOrder ordered unordered index | + masterOrder := Vocabulary eToyVocabulary masterOrderingOfPhraseSymbols. + ordered := SortedCollection sortBlock: [:a :b | a key < b key]. + unordered := SortedCollection sortBlock: [:a :b | a wording < b wording]. + + interfaceList do: [:interface | + index := masterOrder indexOf: interface elementSymbol. + index isZero + ifTrue: [unordered add: interface] + ifFalse: [ordered add: index -> interface]]. + + ^ Array + streamContents: [:stream | + ordered do: [:assoc | stream nextPut: assoc value]. + stream nextPutAll: unordered]! ! + +!Object methodsFor: 'viewer' stamp: 'sw 10/24/2000 11:36'! +newScriptorAround: aPhraseTileMorph + "Sprout a scriptor around aPhraseTileMorph, thus making a new script. This is where generalized scriptors will be threaded in" + + ^ nil! ! + +!Object methodsFor: 'viewer' stamp: 'sw 10/25/2000 17:42'! +offerViewerMenuForEvt: anEvent morph: aMorph + "Offer the viewer's primary menu to the user. aMorph is some morph within the viewer itself, the one within which a mousedown triggered the need for this menu, and it is used only to retrieve the Viewer itself" + + self offerViewerMenuFor: (aMorph ownerThatIsA: StandardViewer) event: anEvent! ! + +!Object methodsFor: 'viewer' stamp: 'sw 8/11/2002 02:03'! +offerViewerMenuFor: aViewer event: evt + "Offer the primary Viewer menu to the user. Copied up from Player code, but most of the functions suggested here don't work for non-Player objects, many aren't even defined, some relate to exploratory sw work not yet reflected in the current corpus. We are early in the life cycle of this method..." + + | aMenu | + aMenu _ MenuMorph new defaultTarget: self. + aMenu addStayUpItem. + aMenu title: '**CAUTION -- UNDER CONSTRUCTION!!** +Many things may not work!! +', self nameForViewer. + (aViewer affordsUniclass and: [self belongsToUniClass not]) ifTrue: + [aMenu add: 'give me a Uniclass' action: #assureUniClass. + aMenu addLine]. + aMenu add: 'choose vocabulary...' target: aViewer action: #chooseVocabulary. + aMenu add: 'choose limit class...' target: aViewer action: #chooseLimitClass. + aMenu add: 'add search pane' target: aViewer action: #addSearchPane. + aMenu balloonTextForLastItem: 'Specify which class should be the most generic one to have its methods shown in this Viewer'. + aMenu addLine. + + self belongsToUniClass ifTrue: + [aMenu add: 'add a new instance variable' target: self selector: #addInstanceVariableIn: argument: aViewer. + aMenu add: 'add a new script' target: aViewer selector: #newPermanentScriptIn: argument: aViewer. + aMenu addLine. + aMenu add: 'make my class be first-class' target: self selector: #makeFirstClassClassIn: argument: aViewer. + aMenu add: 'move my changes up to my superclass' target: self action: #promoteChangesToSuperclass. + aMenu addLine]. + + aMenu add: 'tear off a tile' target: self selector: #launchTileToRefer. + aMenu addLine. + + aMenu add: 'inspect me' target: self selector: #inspect. + aMenu add: 'inspect my class' target: self class action: #inspect. + aMenu addLine. + + aMenu add: 'browse vocabulary' action: #haveFullProtocolBrowsed. + aMenu add: 'inspect this Viewer' target: aViewer action: #inspect. + + aMenu popUpEvent: evt in: aViewer currentWorld + +" + aMenu add: 'references to me' target: aViewer action: #browseReferencesToObject. + aMenu add: 'toggle scratch pane' target: aViewer selector: #toggleScratchPane. + aMenu add: 'make a nascent script for me' target: aViewer selector: #makeNascentScript. + aMenu add: 'rename me' target: aViewer selector: #chooseNewNameForReference. + aMenu add: 'browse full' action: #browseOwnClassFull. + aMenu add: 'browse hierarchy' action: #browseOwnClassHierarchy. + aMenu add: 'set user level...' target: aViewer action: #setUserLevel. + aMenu add: 'browse sub-protocol' action: #browseOwnClassSubProtocol. + aMenu addLine. + +"! ! + +!Object methodsFor: 'viewer' stamp: 'sw 1/22/2001 15:20'! +renameScript: oldSelector + "prompt the user for a new selector and apply it. Presently only works for players" + + self notYetImplemented! ! + +!Object methodsFor: 'viewer' stamp: 'sw 8/10/2004 11:53'! +tilePhrasesForCategory: aCategorySymbol inViewer: aViewer + "Return a collection of phrases for the category." + + | interfaces | + interfaces _ self methodInterfacesForCategory: aCategorySymbol inVocabulary: aViewer currentVocabulary limitClass: aViewer limitClass. + interfaces _ self methodInterfacesInPresentationOrderFrom: interfaces forCategory: aCategorySymbol. + ^ self tilePhrasesForMethodInterfaces: interfaces inViewer: aViewer! ! + +!Object methodsFor: 'viewer' stamp: 'sw 8/10/2004 11:53'! +tilePhrasesForMethodInterfaces: methodInterfaceList inViewer: aViewer + "Return a collection of ViewerLine objects corresponding to the method-interface list provided. The resulting list will be in the same order as the incoming list, but may be smaller if the viewer's vocbulary suppresses some of the methods, or if, in classic tiles mode, the selector requires more arguments than can be handled." + + | toSuppress interfaces resultType itsSelector | + toSuppress _ aViewer currentVocabulary phraseSymbolsToSuppress. + interfaces _ methodInterfaceList reject: [:int | toSuppress includes: int selector]. + Preferences universalTiles ifFalse: "Classic tiles have their limitations..." + [interfaces _ interfaces select: + [:int | + itsSelector _ int selector. + itsSelector numArgs < 2 or: + "The lone two-arg loophole in classic tiles" + [#(color:sees:) includes: itsSelector]]]. + + ^ interfaces collect: + [:aMethodInterface | + ((resultType _ aMethodInterface resultType) notNil and: [resultType ~~ #unknown]) + ifTrue: + [aViewer phraseForVariableFrom: aMethodInterface] + ifFalse: + [aViewer phraseForCommandFrom: aMethodInterface]]! ! + +!Object methodsFor: 'viewer' stamp: 'sw 8/10/2004 12:23'! +tilePhrasesForSelectorList: aList inViewer: aViewer + "Particular to the search facility in viewers. Answer a list, in appropriate order, of ViewerLine objects to put into the viewer." + + | interfaces aVocab | + aVocab _ aViewer currentVocabulary. + interfaces _ self + methodInterfacesInPresentationOrderFrom: + (aList collect: [:aSel | aVocab methodInterfaceForSelector: aSel class: self class]) + forCategory: #search. + ^ self tilePhrasesForMethodInterfaces: interfaces inViewer: aViewer! ! + +!Object methodsFor: 'viewer' stamp: 'sw 5/4/2001 04:51'! +tileToRefer + "Answer a reference tile that comprises an alias to me" + + ^ TileMorph new setToReferTo: self! ! + +!Object methodsFor: 'viewer' stamp: 'sd 3/30/2005 22:04'! +uniqueInstanceVariableNameLike: aString excluding: takenNames + "Answer a nice instance-variable name to be added to the receiver which resembles aString, making sure it does not coincide with any element in takenNames" + + | okBase uniqueName usedNames | + usedNames _ self class allInstVarNamesEverywhere. + usedNames removeAllFoundIn: self class instVarNames. + usedNames addAll: takenNames. + okBase _ Scanner wellFormedInstanceVariableNameFrom: aString. + + uniqueName _ Utilities keyLike: okBase satisfying: + [:aKey | (usedNames includes: aKey) not]. + + ^ uniqueName! ! + +!Object methodsFor: 'viewer' stamp: 'sw 11/21/2001 15:16'! +uniqueNameForReference + "Answer a nice name by which the receiver can be referred to by other objects. At present this uses a global References dictionary to hold the database of references, but in due course this will need to acquire some locality" + + | aName nameSym stem knownClassVars | + (aName _ self uniqueNameForReferenceOrNil) ifNotNil: [^ aName]. + (stem _ self knownName) ifNil: + [stem _ self defaultNameStemForInstances asString]. + stem _ stem select: [:ch | ch isLetter or: [ch isDigit]]. + stem size == 0 ifTrue: [stem _ 'A']. + stem first isLetter ifFalse: + [stem _ 'A', stem]. + stem _ stem capitalized. + knownClassVars _ ScriptingSystem allKnownClassVariableNames. + aName _ Utilities keyLike: stem satisfying: + [:jinaLake | + nameSym _ jinaLake asSymbol. + ((References includesKey: nameSym) not and: + [(Smalltalk includesKey: nameSym) not]) and: + [(knownClassVars includes: nameSym) not]]. + + References at: (aName _ aName asSymbol) put: self. + ^ aName! ! + +!Object methodsFor: 'viewer' stamp: 'md 1/17/2006 17:58'! +uniqueNameForReferenceFrom: proposedName + "Answer a satisfactory symbol, similar to the proposedName but obeying the rules, to represent the receiver" + + | aName nameSym stem okay | + proposedName = self uniqueNameForReferenceOrNil + ifTrue: [^ proposedName]. "No change" + + stem _ proposedName select: [:ch | ch isLetter or: [ch isDigit]]. + stem size == 0 ifTrue: [stem _ 'A']. + stem first isLetter ifFalse: + [stem _ 'A', stem]. + stem _ stem capitalized. + aName _ Utilities keyLike: stem satisfying: + [:jinaLake | + nameSym _ jinaLake asSymbol. + okay _ true. + (self class bindingOf: nameSym) ifNotNil: [okay _ false "don't use it"]. + okay]. + ^ aName asSymbol! ! + +!Object methodsFor: 'viewer' stamp: 'sw 3/15/2004 23:01'! +uniqueNameForReferenceOrNil + "If the receiver has a unique name for reference, return it here, else return nil" + + ^ References keyAtValue: self ifAbsent: [nil]! ! + +!Object methodsFor: 'viewer' stamp: 'ar 5/16/2001 01:40'! +updateThresholdForGraphicInViewerTab + "When a Viewer is open on the receiver, its tab needs some graphic to show to the user. Computing this graphic can take quite some time so we want to make the update frequency depending on how long it takes to compute the thumbnail. The threshold returned by this method defines that the viewer will update at most every 'threshold * timeItTakesToDraw' milliseconds. Thus, if the time for computing the receiver's thumbnail is 200 msecs and the the threshold is 10, the viewer will update at most every two seconds." + ^20 "seems to be a pretty good general choice"! ! + +!Object methodsFor: 'viewer' stamp: 'sw 3/9/2001 13:48'! +usableMethodInterfacesIn: aListOfMethodInterfaces + "Filter aList, returning a subset list of apt phrases" + + ^ aListOfMethodInterfaces +! ! + + +!Object methodsFor: 'world hacking' stamp: 'ar 3/17/2001 23:45'! +couldOpenInMorphic + + "is there an obvious morphic world in which to open a new morph?" + + ^World notNil or: [ActiveWorld notNil]! ! + + +!Object methodsFor: 'private'! +errorImproperStore + "Create an error notification that an improper store was attempted." + + self error: 'Improper store into indexable object'! ! + +!Object methodsFor: 'private'! +errorNonIntegerIndex + "Create an error notification that an improper object was used as an index." + + self error: 'only integers should be used as indices'! ! + +!Object methodsFor: 'private' stamp: 'yo 6/29/2004 11:37'! +errorNotIndexable + "Create an error notification that the receiver is not indexable." + + self error: ('Instances of {1} are not indexable' translated format: {self class name})! ! + +!Object methodsFor: 'private'! +errorSubscriptBounds: index + "Create an error notification that an improper integer was used as an index." + + self error: 'subscript is out of bounds: ' , index printString! ! + +!Object methodsFor: 'private' stamp: 'ar 2/6/2004 14:47'! +primitiveError: aString + "This method is called when the error handling results in a recursion in + calling on error: or halt or halt:." + + | context | + (String + streamContents: + [:s | + s nextPutAll: '***System error handling failed***'. + s cr; nextPutAll: aString. + context _ thisContext sender sender. + 20 timesRepeat: [context == nil ifFalse: [s cr; print: (context _ context sender)]]. + s cr; nextPutAll: '-------------------------------'. + s cr; nextPutAll: 'Type CR to enter an emergency evaluator.'. + s cr; nextPutAll: 'Type any other character to restart.']) + displayAt: 0 @ 0. + [Sensor keyboardPressed] whileFalse. + Sensor keyboard = Character cr ifTrue: [Transcripter emergencyEvaluator]. + Smalltalk isMorphic + ifTrue: [World install "init hands and redisplay"] + ifFalse: [ScheduledControllers searchForActiveController]! ! + +!Object methodsFor: 'private'! +species + "Answer the preferred class for reconstructing the receiver. For example, + collections create new collections whenever enumeration messages such as + collect: or select: are invoked. The new kind of collection is determined by + the species of the original collection. Species and class are not always the + same. For example, the species of Interval is Array." + + ^self class! ! + +!Object methodsFor: 'private'! +storeAt: offset inTempFrame: aContext + "This message had to get sent to an expression already on the stack + as a Block argument being accessed by the debugger. + Just re-route it to the temp frame." + ^ aContext tempAt: offset put: self! ! + +"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! + +Object class + instanceVariableNames: ''! + +!Object class methodsFor: '*Pinesoft-Widgets' stamp: 'gvc 4/17/2007 17:40'! +taskbarIcon + "Answer the icon for an instance of the receiver in a task bar + or nil for the default." + + ^nil! ! + + +!Object class methodsFor: '*magritte-model-accessing' stamp: 'lr 3/27/2006 15:47'! +description + ^ MADescriptionBuilder for: self! ! + + +!Object class methodsFor: 'class initialization' stamp: 'ar 2/11/2001 02:00'! +flushDependents + DependentsFields keysAndValuesDo:[:key :dep| + key ifNotNil:[key removeDependent: nil]. + ]. + DependentsFields finalizeValues.! ! + +!Object class methodsFor: 'class initialization' stamp: 'rw 2/10/2002 13:09'! +flushEvents + "Object flushEvents" + + EventManager flushEvents. ! ! + +!Object class methodsFor: 'class initialization' stamp: 'rww 10/2/2001 07:35'! +initialize + "Object initialize" + DependentsFields ifNil:[self initializeDependentsFields].! ! + +!Object class methodsFor: 'class initialization' stamp: 'ar 2/11/2001 01:41'! +initializeDependentsFields + "Object initialize" + DependentsFields _ WeakIdentityKeyDictionary new. +! ! + +!Object class methodsFor: 'class initialization' stamp: 'ar 2/11/2001 01:45'! +reInitializeDependentsFields + "Object reInitializeDependentsFields" + | oldFields | + oldFields _ DependentsFields. + DependentsFields _ WeakIdentityKeyDictionary new. + oldFields keysAndValuesDo:[:obj :deps| + deps do:[:d| obj addDependent: d]]. +! ! + + +!Object class methodsFor: 'documentation'! +howToModifyPrimitives + "You are allowed to write methods which specify primitives, but please use + caution. If you make a subclass of a class which contains a primitive method, + the subclass inherits the primitive. The message which is implemented + primitively may be overridden in the subclass (E.g., see at:put: in String's + subclass Symbol). The primitive behavior can be invoked using super (see + Symbol string:). + + A class which attempts to mimic the behavior of another class without being + its subclass may or may not be able to use the primitives of the original class. + In general, if the instance variables read or written by a primitive have the + same meanings and are in the same fields in both classes, the primitive will + work. + + For certain frequently used 'special selectors', the compiler emits a + send-special-selector bytecode instead of a send-message bytecode. + Special selectors were created because they offer two advantages. Code + which sends special selectors compiles into fewer bytes than normal. For + some pairs of receiver classes and special selectors, the interpreter jumps + directly to a primitive routine without looking up the method in the class. + This is much faster than a normal message lookup. + + A selector which is a special selector solely in order to save space has a + normal behavior. Methods whose selectors are special in order to + gain speed contain the comment, 'No Lookup'. When the interpreter + encounters a send-special-selector bytecode, it checks the class of the + receiver and the selector. If the class-selector pair is a no-lookup pair, + then the interpreter swiftly jumps to the routine which implements the + corresponding primitive. (A special selector whose receiver is not of the + right class to make a no-lookup pair, is looked up normally). The pairs are + listed below. No-lookup methods contain a primitive number specification, + , which is redundant. Since the method is not normally looked + up, deleting the primitive number specification cannot prevent this + primitive from running. If a no-lookup primitive fails, the method is looked + up normally, and the expressions in it are executed. + + No Lookup pairs of (class, selector) + + SmallInteger with any of + - * / \\ bitOr: bitShift: bitAnd: // + SmallInteger with any of = ~= > < >= <= + Any class with == + Any class with @ + Point with either of x y + ContextPart with blockCopy: + BlockContext with either of value value: + " + + self error: 'comment only'! ! + +!Object class methodsFor: 'documentation'! +whatIsAPrimitive + "Some messages in the system are responded to primitively. A primitive + response is performed directly by the interpreter rather than by evaluating + expressions in a method. The methods for these messages indicate the + presence of a primitive response by including before the + first expression in the method. + + Primitives exist for several reasons. Certain basic or 'primitive' + operations cannot be performed in any other way. Smalltalk without + primitives can move values from one variable to another, but cannot add two + SmallIntegers together. Many methods for arithmetic and comparison + between numbers are primitives. Some primitives allow Smalltalk to + communicate with I/O devices such as the disk, the display, and the keyboard. + Some primitives exist only to make the system run faster; each does the same + thing as a certain Smalltalk method, and its implementation as a primitive is + optional. + + When the Smalltalk interpreter begins to execute a method which specifies a + primitive response, it tries to perform the primitive action and to return a + result. If the routine in the interpreter for this primitive is successful, + it will return a value and the expressions in the method will not be evaluated. + If the primitive routine is not successful, the primitive 'fails', and the + Smalltalk expressions in the method are executed instead. These + expressions are evaluated as though the primitive routine had not been + called. + + The Smalltalk code that is evaluated when a primitive fails usually + anticipates why that primitive might fail. If the primitive is optional, the + expressions in the method do exactly what the primitive would have done (See + Number @). If the primitive only works on certain classes of arguments, the + Smalltalk code tries to coerce the argument or appeals to a superclass to find + a more general way of doing the operation (see SmallInteger +). If the + primitive is never supposed to fail, the expressions signal an error (see + SmallInteger asFloat). + + Each method that specifies a primitive has a comment in it. If the primitive is + optional, the comment will say 'Optional'. An optional primitive that is not + implemented always fails, and the Smalltalk expressions do the work + instead. + + If a primitive is not optional, the comment will say, 'Essential'. Some + methods will have the comment, 'No Lookup'. See Object + howToModifyPrimitives for an explanation of special selectors which are + not looked up. + + For the primitives for +, -, *, and bitShift: in SmallInteger, and truncated + in Float, the primitive constructs and returns a 16-bit + LargePositiveInteger when the result warrants it. Returning 16-bit + LargePositiveIntegers from these primitives instead of failing is + optional in the same sense that the LargePositiveInteger arithmetic + primitives are optional. The comments in the SmallInteger primitives say, + 'Fails if result is not a SmallInteger', even though the implementor has the + option to construct a LargePositiveInteger. For further information on + primitives, see the 'Primitive Methods' part of the chapter on the formal + specification of the interpreter in the Smalltalk book." + + self error: 'comment only'! ! + + +!Object class methodsFor: 'file list services' stamp: 'nk 6/12/2004 11:41'! +fileReaderServicesForDirectory: aFileDirectory + "Backstop" + ^#()! ! + +!Object class methodsFor: 'file list services' stamp: 'nk 6/12/2004 11:30'! +fileReaderServicesForFile: fullName suffix: suffix + "Backstop" + ^#()! ! + +!Object class methodsFor: 'file list services' stamp: 'md 2/15/2006 17:20'! +services + "Backstop" + ^#()! ! + + +!Object class methodsFor: 'instance creation' stamp: 'sw 1/23/2003 09:45'! +categoryForUniclasses + "Answer the default system category into which to place unique-class instances" + + ^ 'UserObjects'! ! + +!Object class methodsFor: 'instance creation' stamp: 'sw 7/28/97 15:56'! +chooseUniqueClassName + | i className | + i _ 1. + [className _ (self name , i printString) asSymbol. + Smalltalk includesKey: className] + whileTrue: [i _ i + 1]. + ^ className! ! + +!Object class methodsFor: 'instance creation' stamp: 'tk 8/22/1998 08:22'! +initialInstance + "Answer the first instance of the receiver, generate an error if there is one already" + "self instanceCount > 0 ifTrue: [self error: 'instance(s) already exist.']." + "Debugging test that is very slow" + ^ self new! ! + +!Object class methodsFor: 'instance creation' stamp: 'sw 5/5/2000 09:30'! +initializedInstance + ^ self new! ! + +!Object class methodsFor: 'instance creation' stamp: 'sw 10/16/2000 10:58'! +instanceOfUniqueClass + "Answer an instance of a unique subclass of the receiver" + + ^ self instanceOfUniqueClassWithInstVarString: '' andClassInstVarString: ''! ! + +!Object class methodsFor: 'instance creation' stamp: 'tk 8/22/1998 08:27'! +instanceOfUniqueClassWithInstVarString: instVarString andClassInstVarString: classInstVarString + "Create a unique class for the receiver, and answer an instance of it" + + ^ (self newUniqueClassInstVars: instVarString + classInstVars: classInstVarString) initialInstance! ! + +!Object class methodsFor: 'instance creation' stamp: 'sw 10/23/1999 22:51'! +isUniClass + ^ false! ! + +!Object class methodsFor: 'instance creation' stamp: 'ajh 5/23/2002 00:35'! +newFrom: aSimilarObject + "Create an object that has similar contents to aSimilarObject. + If the classes have any instance varaibles with the same names, copy them across. + If this is bad for a class, override this method." + + ^ (self isVariable + ifTrue: [self basicNew: aSimilarObject basicSize] + ifFalse: [self basicNew] + ) copySameFrom: aSimilarObject! ! + +!Object class methodsFor: 'instance creation' stamp: 'tk 6/29/1998 12:11'! +newUniqueClassInstVars: instVarString classInstVars: classInstVarString + "Create a unique class for the receiver" + + | aName aClass | + self isSystemDefined ifFalse: + [^ superclass newUniqueClassInstVars: instVarString classInstVars: classInstVarString]. + aName _ self chooseUniqueClassName. + aClass _ self subclass: aName instanceVariableNames: instVarString + classVariableNames: '' poolDictionaries: '' category: self categoryForUniclasses. + classInstVarString size > 0 ifTrue: + [aClass class instanceVariableNames: classInstVarString]. + ^ aClass! ! + +!Object class methodsFor: 'instance creation' stamp: 'sw 7/28/97 15:56'! +newUserInstance + "Answer an instance of an appropriate class to serve as a user object in the containment hierarchy" + + ^ self instanceOfUniqueClass! ! + +!Object class methodsFor: 'instance creation' stamp: 'nk 8/30/2004 07:57'! +readCarefullyFrom: textStringOrStream + "Create an object based on the contents of textStringOrStream. Return an error instead of putting up a SyntaxError window." + + | object | + (Compiler couldEvaluate: textStringOrStream) + ifFalse: [^ self error: 'expected String, Stream, or Text']. + object _ Compiler evaluate: textStringOrStream for: nil + notifying: #error: "signal we want errors" logged: false. + (object isKindOf: self) ifFalse: [self error: self name, ' expected']. + ^object! ! + +!Object class methodsFor: 'instance creation' stamp: 'nk 8/30/2004 07:57'! +readFrom: textStringOrStream + "Create an object based on the contents of textStringOrStream." + + | object | + (Compiler couldEvaluate: textStringOrStream) + ifFalse: [^ self error: 'expected String, Stream, or Text']. + object _ Compiler evaluate: textStringOrStream. + (object isKindOf: self) ifFalse: [self error: self name, ' expected']. + ^object! ! + + +!Object class methodsFor: 'objects from disk' stamp: 'tk 1/8/97'! +createFrom: aSmartRefStream size: varsOnDisk version: instVarList + "Create an instance of me so objects on the disk can be read in. Tricky part is computing the size if variable. Inst vars will be filled in later. " + + ^ self isVariable + ifFalse: [self basicNew] + ifTrue: ["instVarList is names of old class's inst vars plus a version number" + self basicNew: (varsOnDisk - (instVarList size - 1))] +! ! + + +!Object class methodsFor: 'window color' stamp: 'nk 6/10/2004 08:10'! +windowColorSpecification + "Answer a WindowColorSpec object that declares my preference. + This is a backstop for classes that don't otherwise define a preference." + + ^ WindowColorSpec classSymbol: self name + wording: 'Default' brightColor: #white + pastelColor: #white + helpMessage: 'Other windows without color preferences.'! ! + + +!Object class methodsFor: 'private' stamp: 'mir 8/22/2001 15:20'! +releaseExternalSettings + "Do nothing as a default"! ! + + +Object initialize! diff --git a/tests/examplefiles/RegexMatcher.ns2 b/tests/examplefiles/RegexMatcher.ns2 new file mode 100644 index 0000000..1d68f08 --- /dev/null +++ b/tests/examplefiles/RegexMatcher.ns2 @@ -0,0 +1,3518 @@ +Newsqueak2 +'Regex' + +class RegexMatcher main: platform = NewspeakObject ("Ported to NS2 by Ryan Macnak from: + +The Regular Expression Matcher (''The Software'') is Copyright (C) 1996, 1999 Vassili Bykov. +It is provided to the Smalltalk community in hope it will be useful. + +The software is provided free of charge ``as is'', in hope that it will be useful, with ABSOLUTELY NO WARRANTY. The entire risk and all responsibility for the use of the software is with you. Under no circumstances the author may be held responsible for loss of data, loss of profit, or any other damage resulting directly or indirectly from the use of the software, even if the damage is caused by defects in the software. + +You may use this software in any applications you build. + +You may distribute this software with the restrictions that no fee (with the exception of a reasonable fee to cover the cost of distribution media) may be charged for the distribution without a prior written consent of the author, and the software must be distributed with its documentation and copyright notices included and intact. + +You may create and distribute modified versions of the software, such as ports to other Smalltalk dialects or derived work, provided that: +a. any modified version is expressly marked as such and is not misrepresented as the original software; +b. credit is given to the original software in the source code and documentation of the derived work; +c. the copyright notice at the top of this document accompanies copyright notices of any modified version. " +| + + OrderedCollection = platform OrderedCollection. + WriteStream = platform WriteStream. + ReadStream = platform ReadStream. + Dictionary = platform Dictionary. + Association = platform Association. + Transcript = platform Transcript. + Set = platform Set. + Error = platform Error. + MessageNotUnderstood = platform MessageNotUnderstood. + + Cr = Character cr. + Lf = Character lf. + BackslashConstants ::= nil. "?????" + BackslashSpecials ::= nil. "?????" + EscapedLetterSelectors ::= nil. + NamedClassSelectors ::= nil. +| + RxParser initialize. + RxsPredicate initialize. +) +( + +class RxmSpecial = RxmLink ( +"A special node that matches a specific matcher state rather than any input character. +The state is either at-beginning-of-line or at-end-of-line." +| + matchSelector +| +) +('initialize-release' +beBeginningOfLine = ( + + matchSelector:: #atBeginningOfLine +) + +beBeginningOfWord = ( + + matchSelector:: #atBeginningOfWord +) + +beEndOfLine = ( + + matchSelector:: #atEndOfLine +) + +beEndOfWord = ( + matchSelector:: #atEndOfWord +) + +beNotWordBoundary = ( + matchSelector:: #notAtWordBoundary +) + +beWordBoundary = ( + matchSelector:: #atWordBoundary +) + +'matching' +matchAgainst: aMatcher = ( + "Match without consuming any input, if the matcher is + in appropriate state." + + ^(aMatcher perform: matchSelector) + and: [next matchAgainst: aMatcher] +) + +) + +class CompilationError = RegexError ("Regex compilation error") +() + +class SyntaxError = RegexError ("Regex syntax error") +() + +class RxsContextCondition = RxsNode ( +"One of a few special nodes more often representing special state of the match rather than a predicate on a character. The ugly exception is the #any condition which *is* a predicate on a character. + +Instance variables: + kind " +| + kind +| +) +('accessing' +dispatchTo: aBuilder = ( + + ^aBuilder perform: kind +) + +'testing' +isNullable = ( + + ^#syntaxAny ~~ kind +) + +'initialize-release' +beAny = ( + "Matches anything but a newline." + + kind:: #syntaxAny +) + +beBeginningOfLine = ( + "Matches empty string at the beginning of a line." + + kind:: #syntaxBeginningOfLine +) + +beBeginningOfWord = ( + "Matches empty string at the beginning of a word." + + kind:: #syntaxBeginningOfWord +) + +beEndOfLine = ( + "Matches empty string at the end of a line." + + kind:: #syntaxEndOfLine +) + +beEndOfWord = ( + "Matches empty string at the end of a word." + + kind:: #syntaxEndOfWord +) + +beNonWordBoundary = ( + "Analog of \B." + + kind:: #syntaxNonWordBoundary +) + +beWordBoundary = ( + "Analog of \w (alphanumeric plus _)." + + kind:: #syntaxWordBoundary +) + +) + +class RxsBranch piece: p branch: b = RxsNode( +"A Branch is a Piece followed by a Branch or an empty string. + +Instance variables: + piece + branch " +| + piece::= p. branch::= b. +| +) +('accessing' +dispatchTo: aMatcher = ( + "Inform the matcher of the kind of the node, and it + will do whatever it has to." + + ^aMatcher syntaxBranch: self +) + +'optimization' +tryMergingInto: aStream = ( + "Concatenation of a few simple characters can be optimized + to be a plain substring match. Answer the node to resume + syntax tree traversal at. Epsilon node used to terminate the branch + will implement this to answer nil, thus indicating that the branch + has ended." + + piece isAtomic ifFalse: [^self]. + aStream nextPut: piece character. + ^branch isNil + ifTrue: [branch] + ifFalse: [branch tryMergingInto: aStream] +) + +'testing' +isNullable = ( + ^piece isNullable and: [branch isNil or: [branch isNullable]] +) + +) + +class RxmMarker = RxmLink ( +"A marker is used to remember positions of match of certain points of a regular expression. The marker receives an identifying key from the Matcher and uses that key to report positions of successful matches to the Matcher. + +Instance variables: + index Something that makes sense for the Matcher. Received from the latter during initalization and later passed to it to identify the receiver." +| + index +| +) +('matching' +matchAgainst: aMatcher = ( + "If the rest of the link chain matches successfully, report the + position of the stream *before* the match started to the matcher." + + | startPosition | + startPosition:: aMatcher position. + (next matchAgainst: aMatcher) + ifTrue: + [aMatcher markerPositionAt: index add: startPosition. + ^true]. + ^false +) + +) + +class RxMatcher for: syntaxTreeRoot ignoreCase: aBoolean = ( +"This is a recursive regex matcher. Not strikingly efficient, but simple. Also, keeps track of matched subexpressions. The life cycle goes as follows: + +1. Initialization. Accepts a syntax tree (presumably produced by RxParser) and compiles it into a matcher built of other classes in this category. + +2. Matching. Accepts a stream or a string and returns a boolean indicating whether the whole stream or its prefix -- depending on the message sent -- matches the regex. + +3. Subexpression query. After a successful match, and before any other match, the matcher may be queried about the range of specific stream (string) positions that matched to certain parenthesized subexpressions of the original expression. + +Any number of queries may follow a successful match, and any number or matches may follow a successful initialization. + +Note that `matcher' is actually a sort of a misnomer. The actual matcher is a web of Rxm* instances built by RxMatcher during initialization. RxMatcher is just the interface facade of this network. It is also a builder of it, and also provides a stream-like protocol to easily access the stream being matched. + +Slots: + matcher The entry point into the actual matcher. + stream The stream currently being matched against. + markerPositions Positions of markers' matches. + markerCount Number of markers. + lastResult Whether the latest match attempt succeeded or not. + lastChar character last seen in the matcher stream" +| + matcher ignoreCase startOptimizer stream markerPositions markerCount lastResult lastChar +| + + "Compile thyself for the regex with the specified syntax tree. + See comment and `building' protocol in this class and + #dispatchTo: methods in syntax tree components for details + on double-dispatch building. + The argument is supposedly a RxsRegex." + + ignoreCase:: aBoolean. + self buildFrom: syntaxTreeRoot. + startOptimizer:: RxMatchOptimizer for: syntaxTreeRoot ignoreCase: aBoolean. + +) +('private' +allocateMarker = ( + "Answer an integer to use as an index of the next marker." + + markerCount:: markerCount + 1. + ^markerCount +) + +hookBranchOf: regexNode onto: endMarker = ( + "Private - Recurse down the chain of regexes starting at + regexNode, compiling their branches and hooking their tails + to the endMarker node." + + | rest | + rest:: regexNode regex isNil + ifTrue: [nil] + ifFalse: [self hookBranchOf: regexNode regex onto: endMarker]. + ^RxmBranch new + next: ((regexNode branch dispatchTo: self) + pointTailTo: endMarker; + yourself); + alternative: rest; + yourself +) + +isWordChar: aCharacterOrNil = ( + "Answer whether the argument is a word constituent character: + alphanumeric or _." + + ^aCharacterOrNil ~~ nil + and: [aCharacterOrNil isAlphaNumeric] +) + +makeOptional: aMatcher = ( + "Private - Wrap this matcher so that the result would match 0 or 1 + occurrences of the matcher." + + | dummy branch | + dummy:: RxmLink new. + branch:: (RxmBranch new beLoopback) + next: aMatcher; + alternative: dummy. + aMatcher pointTailTo: dummy. + ^branch +) + +makePlus: aMatcher = ( + "Private - Wrap this matcher so that the result would match 1 and more + occurrences of the matcher." + + | loopback | + loopback:: (RxmBranch new beLoopback) + next: aMatcher. + aMatcher pointTailTo: loopback. + ^aMatcher +) + +makeStar: aMatcher = ( + "Private - Wrap this matcher so that the result would match 0 and more + occurrences of the matcher." + + | dummy detour loopback | + dummy:: RxmLink new. + detour:: RxmBranch new + next: aMatcher; + alternative: dummy. + loopback:: (RxmBranch new beLoopback) + next: aMatcher; + alternative: dummy. + aMatcher pointTailTo: loopback. + ^detour +) + +proceedSearchingStream: aStream = ( + + | position | + position:: aStream position. + [aStream atEnd] whileFalse: + [self tryMatch ifTrue: [^true]. + aStream position: position. + lastChar:: aStream next. + position:: aStream position]. + "Try match at the very stream end too!" + self tryMatch ifTrue: [^true]. + ^false +) + +tryMatch = ( + "Match thyself against the current stream." + + markerPositions:: Array new: markerCount. + 1 to: markerCount do: [:i | markerPositions at: i put: OrderedCollection new]. + startOptimizer == nil + ifTrue: [lastResult:: matcher matchAgainst: self] + ifFalse: [lastResult:: (startOptimizer canStartMatch: stream peek in: self) + and: [matcher matchAgainst: self]]. + ^lastResult +) + +'testing' +atBeginningOfLine = ( + ^self position = 0 or: [lastChar = Cr] +) + +atBeginningOfWord = ( + ^(self isWordChar: lastChar) not + and: [self isWordChar: stream peek] +) + +atEndOfLine = ( + ^self atEnd or: [stream peek = Cr] +) + +atEndOfWord = ( + ^(self isWordChar: lastChar) + and: [(self isWordChar: stream peek) not] +) + +atWordBoundary = ( + ^(self isWordChar: lastChar) + xor: (self isWordChar: stream peek) +) + +notAtWordBoundary = ( + ^self atWordBoundary not +) + +supportsSubexpressions = ( + ^true +) + +'streaming' +atEnd = ( + ^stream atEnd +) + +next = ( + lastChar:: stream next. + ^lastChar +) + +position = ( + ^stream position +) + +'accessing' +buildFrom: aSyntaxTreeRoot = ( + "Private - Entry point of matcher build process." + + markerCount:: 0. "must go before #dispatchTo: !" + matcher:: aSyntaxTreeRoot dispatchTo: self. + matcher terminateWith: RxmTerminator new +) + +matches: aString = ( + "Match against a string." + ^self matchesStream: aString readStream +) + +matchesPrefix: aString = ( + "Match against a string." + + ^self matchesStreamPrefix: aString readStream +) + +matchesStream: theStream = ( + "Match thyself against a positionable stream." + + ^(self matchesStreamPrefix: theStream) + and: [stream atEnd] +) + +matchesStreamPrefix: theStream = ( + "Match thyself against a positionable stream." + + stream:: theStream. + lastChar:: nil. + ^self tryMatch +) + +search: aString = ( + "Search the string for occurrence of something matching myself. + Answer a Boolean indicating success." + + ^self searchStream: aString readStream +) + +searchStream: aStream = ( + "Search the stream for occurrence of something matching myself. + After the search has occurred, stop positioned after the end of the + matched substring. Answer a Boolean indicating success." + + | position | + stream:: aStream. + lastChar:: nil. + position:: aStream position. + [aStream atEnd] whileFalse: + [self tryMatch ifTrue: [^true]. + aStream position: position. + lastChar:: aStream next. + position:: aStream position]. + "Try match at the very stream end too!" + self tryMatch ifTrue: [^true]. + ^false +) + +subBeginning: subIndex = ( + ^markerPositions at: subIndex * 2 - 1 +) + +subEnd: subIndex = ( + ^markerPositions at: subIndex * 2 +) + +subexpression: subIndex = ( + "Answer a string that matched the subexpression at the given index. + If there are multiple matches, answer the last one. + If there are no matches, answer nil. + (NB: it used to answer an empty string but I think nil makes more sense)." + + | matches | + matches:: self subexpressions: subIndex. + ^matches isEmpty ifTrue: [nil] ifFalse: [matches last] +) + +subexpressionCount = ( + ^markerCount // 2 +) + +subexpressions: subIndex = ( + "Answer an array of all matches of the subexpression at the given index. + The answer is always an array; it is empty if there are no matches." + + | originalPosition startPositions stopPositions reply | + originalPosition:: stream position. + startPositions:: self subBeginning: subIndex. + stopPositions:: self subEnd: subIndex. + (startPositions isEmpty or: [stopPositions isEmpty]) ifTrue: [^Array new]. + reply:: OrderedCollection new. + startPositions with: stopPositions do: + [:start :stop | + stream position: start. + reply add: (stream next: stop - start)]. + stream position: originalPosition. + ^reply asArray +) + +'match enumeration' +copy: aString replacingMatchesWith: replacementString = ( + "Copy , except for the matches. Replace each match with ." + + | answer | + answer:: (String new: 40) writeStream. + self + copyStream: aString readStream + to: answer + replacingMatchesWith: replacementString. + ^answer contents +) + +copy: aString translatingMatchesUsing: aBlock = ( + "Copy , except for the matches. For each match, evaluate passing the matched substring as the argument. Expect the block to answer a String, and replace the match with the answer." + + | answer | + answer:: (String new: 40) writeStream. + self copyStream: aString readStream to: answer translatingMatchesUsing: aBlock. + ^answer contents +) + +copyStream: aStream to: writeStream replacingMatchesWith: aString = ( + "Copy the contents of on the , except for the matches. Replace each match with ." + + | searchStart matchStart matchEnd | + stream:: aStream. + lastChar:: nil. + [searchStart:: aStream position. + self proceedSearchingStream: aStream] whileTrue: + [matchStart:: (self subBeginning: 1) first. + matchEnd:: (self subEnd: 1) first. + aStream position: searchStart. + searchStart to: matchStart - 1 do: + [:ignoredPos | writeStream nextPut: aStream next]. + writeStream nextPutAll: aString. + aStream position: matchEnd. + "Be extra careful about successful matches which consume no input. + After those, make sure to advance or finish if already at end." + matchEnd = searchStart ifTrue: + [aStream atEnd + ifTrue: [^self "rest after end of whileTrue: block is a no-op if atEnd"] + ifFalse: [writeStream nextPut: aStream next]]]. + aStream position: searchStart. + [aStream atEnd] whileFalse: [writeStream nextPut: aStream next] +) + +copyStream: aStream to: writeStream translatingMatchesUsing: aBlock = ( + "Copy the contents of on the , except for the matches. For each match, evaluate passing the matched substring as the argument. Expect the block to answer a String, and write the answer to in place of the match." + + | searchStart matchStart matchEnd match | + stream:: aStream. + lastChar:: nil. + [searchStart:: aStream position. + self proceedSearchingStream: aStream] whileTrue: + [matchStart:: (self subBeginning: 1) first. + matchEnd:: (self subEnd: 1) first. + aStream position: searchStart. + searchStart to: matchStart - 1 do: + [:ignoredPos | writeStream nextPut: aStream next]. + match:: (String new: matchEnd - matchStart + 1) writeStream. + matchStart to: matchEnd - 1 do: + [:ignoredPos | match nextPut: aStream next]. + writeStream nextPutAll: (aBlock value: match contents). + "Be extra careful about successful matches which consume no input. + After those, make sure to advance or finish if already at end." + matchEnd = searchStart ifTrue: + [aStream atEnd + ifTrue: [^self "rest after end of whileTrue: block is a no-op if atEnd"] + ifFalse: [writeStream nextPut: aStream next]]]. + aStream position: searchStart. + [aStream atEnd] whileFalse: [writeStream nextPut: aStream next] +) + +matchesIn: aString = ( + "Search aString repeatedly for the matches of the receiver. Answer an OrderedCollection of all matches (substrings)." + + | result | + result:: OrderedCollection new. + self + matchesOnStream: aString readStream + do: [:match | result add: match]. + ^result +) + +matchesIn: aString collect: aBlock = ( + "Search aString repeatedly for the matches of the receiver. Evaluate aBlock for each match passing the matched substring as the argument, collect evaluation results in an OrderedCollection, and return in. The following example shows how to use this message to split a string into words." + "'\w+' asRegex matchesIn: 'Now is the Time' collect: [:each | each asLowercase]" + + | result | + result:: OrderedCollection new. + self + matchesOnStream: aString readStream + do: [:match | result add: (aBlock value: match)]. + ^result +) + +matchesIn: aString do: aBlock = ( + "Search aString repeatedly for the matches of the receiver. + Evaluate aBlock for each match passing the matched substring + as the argument." + + self + matchesOnStream: aString readStream + do: aBlock +) + +matchesOnStream: aStream = ( + + | result | + result:: OrderedCollection new. + self + matchesOnStream: aStream + do: [:match | result add: match]. + ^result +) + +matchesOnStream: aStream collect: aBlock = ( + | result | + result:: OrderedCollection new. + self + matchesOnStream: aStream + do: [:match | result add: (aBlock value: match)]. + ^result +) + +matchesOnStream: aStream do: aBlock = ( + "Be extra careful about successful matches which consume no input. + After those, make sure to advance or finish if already at end." + + | position | + [position:: aStream position. + self searchStream: aStream] whileTrue: + [aBlock value: (self subexpression: 1). + position = aStream position ifTrue: + [aStream atEnd + ifTrue: [^self] + ifFalse: [aStream next]]] +) + +matchingRangesIn: aString = ( + "Search aString repeatedly for the matches of the receiver. Answer an OrderedCollection of ranges of each match (index of first character to: index of last character)." + + | result | + result:: OrderedCollection new. + self + matchesIn: aString + do: [:match | result add: (self position - match size + 1 to: self position)]. + ^result +) + +'privileged' +currentState = ( + "Answer an opaque object that can later be used to restore the + matcher's state (for backtracking)." + + | origPosition origLastChar | + origPosition:: stream position. + origLastChar:: lastChar. + ^ [stream position: origPosition. + lastChar:: origLastChar] +) + +markerPositionAt: anIndex add: position = ( + "Remember position of another instance of the given marker." + + (markerPositions at: anIndex) addFirst: position +) + +restoreState: aBlock = ( + aBlock value +) + +'double dispatch' +syntaxAny = ( + "Double dispatch from the syntax tree. + Create a matcher for any non-null character." + + ^RxmPredicate new + predicate: [:char | char asInteger ~= 0] +) + +syntaxBeginningOfLine = ( + "Double dispatch from the syntax tree. + Create a matcher for beginning-of-line condition." + + ^RxmSpecial new beBeginningOfLine +) + +syntaxBeginningOfWord = ( + "Double dispatch from the syntax tree. + Create a matcher for beginning-of-word condition." + + ^RxmSpecial new beBeginningOfWord +) + +syntaxBranch: branchNode = ( + "Double dispatch from the syntax tree. + Branch node is a link in a chain of concatenated pieces. + First build the matcher for the rest of the chain, then make + it for the current piece and hook the rest to it." + + | result next rest | + branchNode branch isNil + ifTrue: [^branchNode piece dispatchTo: self]. + "Optimization: glue a sequence of individual characters into a single string to match." + branchNode piece isAtomic ifTrue: + [result:: WriteStream on: (String new: 40). + next:: branchNode tryMergingInto: result. + result:: result contents. + result size > 1 ifTrue: "worth merging" + [rest:: next notNil + ifTrue: [next dispatchTo: self] + ifFalse: [nil]. + ^(RxmSubstring new substring: result ignoreCase: ignoreCase) + pointTailTo: rest; + yourself]]. + "No optimization possible or worth it, just concatenate all. " + ^(branchNode piece dispatchTo: self) + pointTailTo: (branchNode branch dispatchTo: self); + yourself +) + +syntaxCharSet: charSetNode = ( + "Double dispatch from the syntax tree. + A character set is a few characters, and we either match any of them, + or match any that is not one of them." + + ^RxmPredicate with: (charSetNode predicateIgnoringCase: ignoreCase) +) + +syntaxCharacter: charNode = ( + "Double dispatch from the syntax tree. + We get here when no merging characters into strings was possible." + + | wanted | + wanted:: charNode character. + ^RxmPredicate new predicate: + (ignoreCase + ifTrue: [[:char | char sameAs: wanted]] + ifFalse: [[:char | char = wanted]]) +) + +syntaxEndOfLine = ( + "Double dispatch from the syntax tree. + Create a matcher for end-of-line condition." + + ^RxmSpecial new beEndOfLine +) + +syntaxEndOfWord = ( + "Double dispatch from the syntax tree. + Create a matcher for end-of-word condition." + + ^RxmSpecial new beEndOfWord +) + +syntaxEpsilon = ( + "Double dispatch from the syntax tree. Match empty string. This is unlikely + to happen in sane expressions, so we'll live without special epsilon-nodes." + + ^RxmSubstring new + substring: String new + ignoreCase: ignoreCase +) + +syntaxMessagePredicate: messagePredicateNode = ( + "Double dispatch from the syntax tree. + Special link can handle predicates." + + ^messagePredicateNode negated + ifTrue: [RxmPredicate new bePerformNot: messagePredicateNode selector] + ifFalse: [RxmPredicate new bePerform: messagePredicateNode selector] +) + +syntaxNonWordBoundary = ( + "Double dispatch from the syntax tree. + Create a matcher for the word boundary condition." + + ^RxmSpecial new beNotWordBoundary +) + +syntaxPiece: pieceNode = ( + "Double dispatch from the syntax tree. + Piece is an atom repeated a few times. Take care of a special + case when the atom is repeated just once." + + | atom | + atom:: pieceNode atom dispatchTo: self. + ^pieceNode isSingular + ifTrue: [atom] + ifFalse: [pieceNode isStar + ifTrue: [self makeStar: atom] + ifFalse: [pieceNode isPlus + ifTrue: [self makePlus: atom] + ifFalse: [pieceNode isOptional + ifTrue: [self makeOptional: atom] + ifFalse: [CompilationError signal: + 'repetitions are not supported by RxMatcher']]]] +) + +syntaxPredicate: predicateNode = ( + "Double dispatch from the syntax tree. + A character set is a few characters, and we either match any of them, + or match any that is not one of them." + + ^RxmPredicate with: predicateNode predicate +) + +syntaxRegex: regexNode = ( + "Double dispatch from the syntax tree. + Regex node is a chain of branches to be tried. Should compile this + into a bundle of parallel branches, between two marker nodes." + + | startIndex endIndex endNode alternatives | + startIndex:: self allocateMarker. + endIndex:: self allocateMarker. + endNode:: RxmMarker new index: endIndex. + alternatives:: self hookBranchOf: regexNode onto: endNode. + ^(RxmMarker new index: startIndex) + pointTailTo: alternatives; + yourself +) + +syntaxWordBoundary = ( + "Double dispatch from the syntax tree. + Create a matcher for the word boundary condition." + + ^RxmSpecial new beWordBoundary +) + +) : ( +'as yet unclassified' +for: aRegex = ( + ^self for: aRegex ignoreCase: false +) + +forString: aString = ( + "Create and answer a matcher that will match the regular expression + `aString'." + + ^self for: (RxParser new parse: aString) +) + +forString: aString ignoreCase: aBoolean = ( + "Create and answer a matcher that will match the regular expression + `aString'." + + ^self for: (RxParser new parse: aString) ignoreCase: aBoolean +) + +) + +class RxMatchOptimizer for: aRegex ignoreCase: aBoolean = ( +"A match start optimizer, handy for searching a string. Takes a regex syntax tree and sets itself up so that prefix characters or matcher states that cannot start a match are later recognized with #canStartMatch:in: method. + +Used by RxMatcher, but can be used by other matchers (if implemented) as well." +| + ignoreCase prefixes nonPrefixes conditions testBlock methodPredicates nonMethodPredicates predicates nonPredicates +| + ignoreCase:: aBoolean. + prefixes:: Set new: 10. + nonPrefixes:: Set new: 10. + conditions:: Set new: 3. + methodPredicates:: Set new: 3. + nonMethodPredicates:: Set new: 3. + predicates:: Set new: 3. + nonPredicates:: Set new: 3. + aRegex dispatchTo: self. "If the whole expression is nullable, + end-of-line is an implicit can-match condition!" + aRegex isNullable ifTrue: [conditions add: #atEndOfLine]. + testBlock:: self determineTestMethod +) +('accessing' +canStartMatch: aCharacter in: aMatcher = ( + "Answer whether a match could commence at the given lookahead + character, or in the current state of . True answered + by this method does not mean a match will definitly occur, while false + answered by this method *does* guarantee a match will never occur." + + aCharacter isNil ifTrue: [^true]. + ^testBlock == nil or: [testBlock value: aCharacter value: aMatcher] +) + +conditionTester = ( + "#any condition is filtered at the higher level; + it cannot appear among the conditions here." + + | matchCondition | + conditions isEmpty ifTrue: [^nil]. + conditions size = 1 ifTrue: + [matchCondition:: conditions detect: [:ignored | true]. + "Special case all of the possible conditions." + #atBeginningOfLine = matchCondition ifTrue: [^[:c :matcher | matcher atBeginningOfLine]]. + #atEndOfLine = matchCondition ifTrue: [^[:c :matcher | matcher atEndOfLine]]. + #atBeginningOfWord = matchCondition ifTrue: [^[:c :matcher | matcher atBeginningOfWord]]. + #atEndOfWord = matchCondition ifTrue: [^[:c :matcher | matcher atEndOfWord]]. + #atWordBoundary = matchCondition ifTrue: [^[:c :matcher | matcher atWordBoundary]]. + #notAtWordBoundary = matchCondition ifTrue: [^[:c :matcher | matcher notAtWordBoundary]]. + CompilationError signal: 'invalid match condition']. + "More than one condition. Capture them as an array in scope." + matchCondition:: conditions asArray. + ^[:c :matcher | + matchCondition contains: + [:conditionSelector | + matcher perform: conditionSelector]] +) + +methodPredicateTester = ( + | p selector | + methodPredicates isEmpty ifTrue: [^nil]. + p:: self optimizeSet: methodPredicates. "also allows copying closures" + ^p size = 1 + ifTrue: + ["might be a pretty common case" + selector:: p first. + [:char :matcher | + RxParser doHandlingMessageNotUnderstood: + [char perform: selector]]] + ifFalse: + [[:char :m | + RxParser doHandlingMessageNotUnderstood: + [p contains: [:sel | char perform: sel]]]] +) + +nonMethodPredicateTester = ( + | p selector | + nonMethodPredicates isEmpty ifTrue: [^nil]. + p:: self optimizeSet: nonMethodPredicates. "also allows copying closures" + ^p size = 1 + ifTrue: + [selector:: p first. + [:char :matcher | + RxParser doHandlingMessageNotUnderstood: + [(char perform: selector) not]]] + ifFalse: + [[:char :m | + RxParser doHandlingMessageNotUnderstood: + [p contains: [:sel | (char perform: sel) not]]]] +) + +'double dispatch' +syntaxAny = ( + "Any special char is among the prefixes." + + conditions add: #any +) + +syntaxBeginningOfLine = ( + "Beginning of line is among the prefixes." + + conditions add: #atBeginningOfLine +) + +syntaxBeginningOfWord = ( + "Beginning of line is among the prefixes." + + conditions add: #atBeginningOfWord +) + +syntaxBranch: branchNode = ( + "If the head piece of the branch is transparent (allows 0 matches), + we must recurse down the branch. Otherwise, just the head atom + is important." + + (branchNode piece isNullable and: [branchNode branch notNil]) + ifTrue: [branchNode branch dispatchTo: self]. + branchNode piece dispatchTo: self +) + +syntaxCharSet: charSetNode = ( + "All these (or none of these) characters is the prefix." + + charSetNode isNegated + ifTrue: [nonPrefixes addAll: (charSetNode enumerableSetIgnoringCase: ignoreCase)] + ifFalse: [prefixes addAll: (charSetNode enumerableSetIgnoringCase: ignoreCase)]. + charSetNode hasPredicates ifTrue: + [charSetNode isNegated + ifTrue: [nonPredicates addAll: charSetNode predicates] + ifFalse: [predicates addAll: charSetNode predicates]] +) + +syntaxCharacter: charNode = ( + "This character is the prefix, of one of them." + + prefixes add: charNode character +) + +syntaxEndOfLine = ( + "Beginning of line is among the prefixes." + + conditions add: #atEndOfLine +) + +syntaxEndOfWord = ( + + conditions add: #atEndOfWord +) + +syntaxEpsilon = ( + "Empty string, terminate the recursion (do nothing)." +) + +syntaxMessagePredicate: messagePredicateNode = ( + messagePredicateNode negated + ifTrue: [nonMethodPredicates add: messagePredicateNode selector] + ifFalse: [methodPredicates add: messagePredicateNode selector] +) + +syntaxNonWordBoundary = ( + conditions add: #notAtWordBoundary +) + +syntaxPiece: pieceNode = ( + "Pass on to the atom." + + pieceNode atom dispatchTo: self +) + +syntaxPredicate: predicateNode = ( + + predicates add: predicateNode predicate +) + +syntaxRegex: regexNode = ( + "All prefixes of the regex's branches should be combined. + Therefore, just recurse." + + regexNode branch dispatchTo: self. + regexNode regex notNil + ifTrue: [regexNode regex dispatchTo: self] +) + +syntaxWordBoundary = ( + + conditions add: #atWordBoundary +) + +'private' +determineTestMethod = ( + "Answer a block closure that will work as a can-match predicate. + Answer nil if no viable optimization is possible (too many chars would + be able to start a match)." + + | testers | + (conditions includes: #any) ifTrue: [^nil]. + testers:: OrderedCollection new: 5. + {#prefixTester. #nonPrefixTester. #conditionTester. #methodPredicateTester. #nonMethodPredicateTester. #predicateTester. #nonPredicateTester} + do: + [:selector | + | tester | + tester:: self perform: selector. + tester notNil ifTrue: [testers add: tester]]. + testers isEmpty ifTrue: [^nil]. + testers size = 1 ifTrue: [^testers first]. + testers:: testers asArray. + ^[:char :matcher | testers contains: [:t | t value: char value: matcher]] +) + +nonPredicateTester = ( + + | p pred | + nonPredicates isEmpty ifTrue: [^nil]. + p:: self optimizeSet: nonPredicates. "also allows copying closures" + ^p size = 1 + ifTrue: + [pred:: p first. + [:char :matcher | (pred value: char) not]] + ifFalse: + [[:char :m | p contains: [:some | (some value: char) not]]] +) + +nonPrefixTester = ( + + | np nonPrefixChar | + nonPrefixes isEmpty ifTrue: [^nil]. + np:: self optimizeSet: nonPrefixes. "also allows copying closures" + ^np size = 1 "might be be pretty common case" + ifTrue: + [nonPrefixChar:: np first. + [:char :matcher | char ~= nonPrefixChar]] + ifFalse: [[:char : matcher | (np includes: char) not]] +) + +optimizeSet: aSet = ( + "If a set is small, convert it to array to speed up lookup + (Array has no hashing overhead, beats Set on small number + of elements)." + + ^aSet size < 10 ifTrue: [aSet asArray] ifFalse: [aSet] +) + +predicateTester = ( + | p pred | + predicates isEmpty ifTrue: [^nil]. + p:: self optimizeSet: predicates. "also allows copying closures" + ^p size = 1 + ifTrue: + [pred:: p first. + [:char :matcher | pred value: char]] + ifFalse: + [[:char :m | p contains: [:some | some value: char]]] +) + +prefixTester = ( + + | p prefixChar | + prefixes isEmpty ifTrue: [^nil]. + p:: self optimizeSet: prefixes. "also allows copying closures" + ignoreCase ifTrue: [p:: p collect: [:each | each asUppercase]]. + ^p size = 1 "might be a pretty common case" + ifTrue: + [prefixChar:: p first. + ignoreCase + ifTrue: [[:char :matcher | char sameAs: prefixChar]] + ifFalse: [[:char :matcher | char = prefixChar]]] + ifFalse: + [ignoreCase + ifTrue: [[:char :matcher | p includes: char asUppercase]] + ifFalse: [[:char :matcher | p includes: char]]] +) + +) + +class RxsMessagePredicate selector: s negated: aBoolean = RxsNode ( +"A message predicate represents a condition on a character that is tested (at the match time) by sending a unary message to the character expecting a Boolean answer. + +Instance variables: + selector " +| + selector::= s. + negated::= aBoolean. +| +) +('accessing' +dispatchTo: aBuilder = ( + "Inform the matcher of the kind of the node, and it + will do whatever it has to." + + ^aBuilder syntaxMessagePredicate: self +) + +) + +class RxCharSetParser on: aStream = ( +"I am a parser created to parse the insides of a character set ([...]) construct. I create and answer a collection of 'elements', each being an instance of one of: RxsCharacter, RxsRange, or RxsPredicate." +| + source lookahead elements +| +source: aStream. +lookahead: aStream next. +elements: OrderedCollection new. +) +('accessing' +parse = ( + lookahead = $- ifTrue: + [self addChar: $-. + self match: $-]. + [lookahead isNil] whileFalse: [self parseStep]. + ^elements +) + +'parsing' +addChar: aChar = ( + elements add: (RxsCharacter with: aChar) +) + +addRangeFrom: firstChar to: lastChar = ( + firstChar asInteger > lastChar asInteger ifTrue: + [SyntaxError signal: ' bad character range']. + elements add: (RxsRange from: firstChar to: lastChar) +) + +match: aCharacter = ( + aCharacter = lookahead + ifFalse: [SyntaxError signal: 'unexpected character: ', (String with: lookahead)]. + ^source atEnd + ifTrue: [lookahead: nil] + ifFalse: [lookahead: source next] +) + +parseCharOrRange = ( + | firstChar | + firstChar: lookahead. + self match: firstChar. + lookahead = $- ifTrue: + [self match: $-. + lookahead isNil + ifTrue: [^self addChar: firstChar; addChar: $-] + ifFalse: + [self addRangeFrom: firstChar to: lookahead. + ^self match: lookahead]]. + self addChar: firstChar +) + +parseEscapeChar = ( + self match: $\. + $- = lookahead + ifTrue: [elements add: (RxsCharacter with: $-)] + ifFalse: [elements add: (RxsPredicate forEscapedLetter: lookahead)]. + self match: lookahead +) + +parseNamedSet = ( + | name | + self match: $[; match: $:. + name:: (String with: lookahead), (source upTo: $:). + lookahead:: source next. + self match: $]. + elements add: (RxsPredicate forNamedClass: name) +) + +parseStep = ( + lookahead = $[ ifTrue: + [source peek = $: + ifTrue: [^self parseNamedSet] + ifFalse: [^self parseCharOrRange]]. + lookahead = $\ ifTrue: + [^self parseEscapeChar]. + lookahead = $- ifTrue: + [SyntaxError signal: 'invalid range']. + self parseCharOrRange +) + +) + +class RxsRange from: aChar to: anotherChar = RxsNode ( +"I represent a range of characters as appear in character classes such as + + [a-ZA-Z0-9]. + +I appear in a syntax tree only as an element of RxsCharSet. + +Instance Variables: + + first + last " +| + first ::= aChar. + last ::= anotherChar. +| +) +('accessing' +enumerateTo: aSet ignoringCase: aBoolean = ( + "Add all of the elements I represent to the collection." + + first asInteger to: last asInteger do: + [:charCode | | character | + character:: charCode asCharacter. + aBoolean + ifTrue: + [aSet + add: character asUppercase; + add: character asLowercase] + ifFalse: [aSet add: character]] +) + +'testing' +isEnumerable = ( + + ^true +) + +) + +class RxsNode = ( +"A generic syntax tree node, provides some common responses to the standard tests, as well as tree structure printing -- handy for debugging." + +) +('constants' +indentCharacter = ( + "Normally, #printOn:withIndent: method in subclasses + print several characters returned by this method to indicate + the tree structure." + + ^$+ +) + +'testing' +isAtomic = ( + "Answer whether the node is atomic, i.e. matches exactly one + constant predefined normal character. A matcher may decide to + optimize matching of a sequence of atomic nodes by glueing them + together in a string." + + ^false "tentatively" +) + +isNullable = ( + "True if the node can match an empty sequence of characters." + + ^false "for most nodes" +) + +) + +class RegexError = Error ("Regex error") +() + +class RxsPredicate = RxsNode ( +"This represents a character that satisfies a certain predicate. + +Instance Variables: + + predicate A one-argument block. If it evaluates to the value defined by when it is passed a character, the predicate is considered to match. + negation A one-argument block that is a negation of ." +| + predicate + negation +| +) +('accessing' +dispatchTo: anObject = ( + + ^anObject syntaxPredicate: self +) + +negated = ( + + ^self copy negate +) + +predicateNegation = ( + + ^negation +) + +value: aCharacter = ( + + ^predicate value: aCharacter +) + +'private' +negate = ( + + | tmp | + tmp:: predicate. + predicate:: negation. + negation:: tmp +) + +'testing' +isAtomic = ( + "A predicate is a single character but the character is not known in advance." + + ^false +) + +isEnumerable = ( + + ^false +) + +'initialize-release' +beAlphaNumeric = ( + + predicate:: [:char | char isAlphaNumeric]. + negation:: [:char | char isAlphaNumeric not] +) + +beAlphabetic = ( + + predicate:: [:char | char isAlphabetic]. + negation:: [:char | char isAlphabetic not] +) + +beBackslash = ( + + predicate:: [:char | char == $\]. + negation:: [:char | char ~~ $\] +) + +beControl = ( + + predicate:: [:char | char asInteger < 32]. + negation:: [:char | char asInteger >= 32] +) + +beDigit = ( + + predicate:: [:char | char isDigit]. + negation:: [:char | char isDigit not] +) + +beGraphics = ( + beControl. + negate +) + +beHexDigit = ( + + | hexLetters | + hexLetters:: 'abcdefABCDEF'. + predicate:: [:char | char isDigit or: [hexLetters includes: char]]. + negation:: [:char | char isDigit not and: [(hexLetters includes: char) not]] +) + +beLowercase = ( + + predicate:: [:char | char isLowercase]. + negation:: [:char | char isLowercase not] +) + +beNotDigit = ( + + beDigit. + negate. +) + +beNotSpace = ( + + beSpace. + negate +) + +beNotWordConstituent = ( + + beWordConstituent. + negate. +) + +bePrintable = ( + + beControl. + negate. +) + +bePunctuation = ( + + | punctuationChars | + punctuationChars:: {$.. $,. $!. $?. $;. $:. $". $'. $-. $(. $). $`.}. + predicate:: [:char | punctuationChars includes: char]. + negation:: [:char | (punctuationChars includes: char) not] +) + +beSpace = ( + + predicate:: [:char | char isSeparator]. + negation:: [:char | char isSeparator not] +) + +beUppercase = ( + + predicate:: [:char | char isUppercase]. + negation:: [:char | char isUppercase not] +) + +beWordConstituent = ( + + predicate:: [:char | char isAlphaNumeric]. + negation:: [:char | char isAlphaNumeric not] +) + +) : ( +'as yet unclassified' +forEscapedLetter: aCharacter = ( + + ^self new perform: + (EscapedLetterSelectors + at: aCharacter + ifAbsent: [SyntaxError signal: 'bad backslash escape']) +) + +forNamedClass: aString = ( + + ^self new perform: + (NamedClassSelectors + at: aString + ifAbsent: [SyntaxError signal: 'bad character class name']) +) + +initialize = ( + "self initialize" + + + initializeNamedClassSelectors. + initializeEscapedLetterSelectors. +) + +initializeEscapedLetterSelectors = ( + "self initializeEscapedLetterSelectors" + + (EscapedLetterSelectors:: Dictionary new). + EscapedLetterSelectors + at: $w put: #beWordConstituent; + at: $W put: #beNotWordConstituent; + at: $d put: #beDigit; + at: $D put: #beNotDigit; + at: $s put: #beSpace; + at: $S put: #beNotSpace; + at: $\ put: #beBackslash +) + +initializeNamedClassSelectors = ( + "self initializeNamedClassSelectors" + + (NamedClassSelectors:: Dictionary new). + NamedClassSelectors + at: 'alnum' put: #beAlphaNumeric; + at: 'alpha' put: #beAlphabetic; + at: 'cntrl' put: #beControl; + at: 'digit' put: #beDigit; + at: 'graph' put: #beGraphics; + at: 'lower' put: #beLowercase; + at: 'print' put: #bePrintable; + at: 'punct' put: #bePunctuation; + at: 'space' put: #beSpace; + at: 'upper' put: #beUppercase; + at: 'xdigit' put: #beHexDigit +) + +) + +class RxsEpsilon = RxsNode ( +"This is an empty string. It terminates some of the recursive constructs." + +) +('building' +dispatchTo: aBuilder = ( + "Inform the matcher of the kind of the node, and it + will do whatever it has to." + + ^aBuilder syntaxEpsilon +) + +'testing' +isNullable = ( + "See comment in the superclass." + + ^true +) + +) + +class RxsCharacter with: aCharacter = RxsNode ( +"A character is a literal character that appears either in the expression itself or in a character set within an expression. + +Instance variables: + character " +| + character ::= aCharacter. +| +) +('accessing' +dispatchTo: aMatcher = ( + "Inform the matcher of the kind of the node, and it + will do whatever it has to." + + ^aMatcher syntaxCharacter: self +) + +enumerateTo: aSet ignoringCase: aBoolean = ( + aBoolean + ifTrue: + [aSet + add: character asUppercase; + add: character asLowercase] + ifFalse: [aSet add: character] +) + +'testing' +isAtomic = ( + "A character is always atomic." + + ^true +) + +isEnumerable = ( + ^true +) + +isNullable = ( + ^false +) + +) + +class RxParser = ( +"The regular expression parser. Translates a regular expression read from a stream into a parse tree. ('accessing' protocol). The tree can later be passed to a matcher initialization method. All other classes in this category implement the tree. Refer to their comments for any details. + +Instance variables: + input A stream with the regular expression being parsed. + lookahead " +| + input lookahead +| +) +('accessing' +parse: aString = ( + "Parse input from a string . + On success, answers an RxsRegex -- parse tree root. + On error, raises `RxParser syntaxErrorSignal' with the current + input stream position as the parameter." + + ^self parseStream: (ReadStream on: aString) +) + +parseStream: aStream = ( + "Parse an input from a character stream . + On success, answers an RxsRegex -- parse tree root. + On error, raises `RxParser syntaxErrorSignal' with the current + input stream position as the parameter." + + | tree | + input:: aStream. + lookahead:: nil. + self match: nil. + tree:: self regex. + self match: #epsilon. + ^tree +) + +piece = ( + " ::= | * | + | ?" + + | atom errorMessage | + errorMessage:: ' nullable closure'. + atom:: self atom. + lookahead = $* ifTrue: + [self next. + atom isNullable ifTrue: [self signalParseError: errorMessage]. + ^RxsPiece starAtom: atom]. + lookahead = $+ ifTrue: + [self next. + atom isNullable ifTrue: [self signalParseError: errorMessage]. + ^RxsPiece plusAtom: atom]. + lookahead = $? ifTrue: + [self next. + atom isNullable ifTrue: [self signalParseError: errorMessage]. + ^RxsPiece optionalAtom: atom]. + ^RxsPiece atom: atom +) + +regex = ( + " ::= e | `|' " + + | branch regex | + branch:: self branch. + (lookahead = #epsilon or: [lookahead = $)]) + ifTrue: [regex:: nil] + ifFalse: + [self match: $|. + regex:: self regex]. + ^RxsRegex branch: branch regex: regex +) + +'private' +characterSetFrom: setSpec = ( + " is what goes between the brackets in a charset regex + (a String). Make a string containing all characters the spec specifies. + Spec is never empty." + + | negated spec | + spec:: ReadStream on: setSpec. + spec peek = $^ + ifTrue: [negated:: true. + spec next] + ifFalse: [negated:: false]. + ^RxsCharSet elements: (RxCharSetParser on: spec) parse negated: negated +) + +ifSpecial: aCharacter then: aBlock = ( + "If the character is such that it defines a special node when follows a $\, + then create that node and evaluate aBlock with the node as the parameter. + Otherwise just return." + + | classAndSelector | + classAndSelector:: BackslashSpecials at: aCharacter ifAbsent: [^self]. + ^aBlock value: (classAndSelector key new perform: classAndSelector value) +) + +inputUpTo: aCharacter errorMessage: aString = ( + "Accumulate input stream until is encountered + and answer the accumulated chars as String, not including + . Signal error if end of stream is encountered, + passing as the error description." + + | accumulator | + accumulator:: WriteStream on: (String new: 20). + [lookahead ~= aCharacter and: [lookahead ~= #epsilon]] + whileTrue: + [accumulator nextPut: lookahead. + self next]. + lookahead = #epsilon ifTrue: [self signalParseError: aString]. + ^accumulator contents +) + +inputUpTo: aCharacter nestedOn: anotherCharacter errorMessage: aString = ( + "Accumulate input stream until is encountered + and answer the accumulated chars as String, not including + . Signal error if end of stream is encountered, + passing as the error description." + + | accumulator nestLevel | + accumulator:: WriteStream on: (String new: 20). + nestLevel:: 0. + [lookahead ~= aCharacter or: [nestLevel > 0]] whileTrue: + [#epsilon = lookahead ifTrue: [self signalParseError: aString]. + accumulator nextPut: lookahead. + lookahead = anotherCharacter ifTrue: [nestLevel:: nestLevel + 1]. + lookahead = aCharacter ifTrue: [nestLevel:: nestLevel - 1]. + self next]. + ^accumulator contents +) + +match: aCharacter = ( + " MUST match the current lookeahead. + If this is the case, advance the input. Otherwise, blow up." + + aCharacter ~= lookahead + ifTrue: [^self signalParseError]. "does not return" + self next +) + +next = ( + "Advance the input storing the just read character + as the lookahead." + + input atEnd + ifTrue: [lookahead:: #epsilon] + ifFalse: [lookahead:: input next] +) + +signalParseError = ( + self class signalSyntaxException: 'Regex syntax error' +) + +signalParseError: aString = ( + self class signalSyntaxException: aString +) + +'recursive descent' +atom = ( + "An atom is one of a lot of possibilities, see below." + + | atom | + (lookahead = #epsilon or: + [lookahead = $| or: + [lookahead = $) + or: [lookahead = $* or: [lookahead = $+ or: [lookahead = $?]]]]]) + ifTrue: [^RxsEpsilon new]. + lookahead = $( ifTrue: + [" ::= '(' ')' " + + self match: $(. + atom:: self regex. + self match: $). + ^atom]. + lookahead = $[ ifTrue: + [" ::= '[' ']' " + + self match: $[. + atom:: self characterSet. + self match: $]. + ^atom]. + lookahead = $: ifTrue: + [" ::= ':' ':' " + + self match: $:. + atom:: self messagePredicate. + self match: $:. + ^atom]. + lookahead = $. ifTrue: + ["any non-whitespace character" + + self next. + ^RxsContextCondition new beAny]. + lookahead = $^ ifTrue: + ["beginning of line condition" + + self next. + ^RxsContextCondition new beBeginningOfLine]. + lookahead = $$ ifTrue: + ["end of line condition" + + self next. + ^RxsContextCondition new beEndOfLine]. + lookahead = $\ ifTrue: + [" ::= '\' " + self next. + lookahead = #epsilon ifTrue: + [self signalParseError: 'bad quotation']. + (BackslashConstants includesKey: lookahead) ifTrue: + [atom:: RxsCharacter with: (BackslashConstants at: lookahead). + self next. + ^atom]. + self ifSpecial: lookahead + then: [:node | self next. ^node]]. + "If passed through the above, the following is a regular character." + atom:: RxsCharacter with: lookahead. + self next. + ^atom +) + +branch = ( + " ::= e | " + + | piece branch | + piece:: self piece. + (lookahead = #epsilon or: [lookahead = $| or: [lookahead = $) ]]) + ifTrue: [branch:: nil] + ifFalse: [branch:: self branch]. + ^RxsBranch piece: piece branch: branch +) + +characterSet = ( + "Match a range of characters: something between `[' and `]'. + Opening bracked has already been seen, and closing should + not be consumed as well. Set spec is as usual for + sets in regexes." + + | spec errorMessage | + errorMessage:: ' no terminating "]"'. + spec:: self inputUpTo: $] nestedOn: $[ errorMessage: errorMessage. + (spec isEmpty or: [spec = '^']) ifTrue: "This ']' was literal." + [self next. + spec:: spec, ']', (self inputUpTo: $] nestedOn: $[ errorMessage: errorMessage)]. + ^self characterSetFrom: spec +) + +messagePredicate = ( + "Match a message predicate specification: a selector (presumably + understood by a Character) enclosed in :'s ." + + | spec negated | + spec:: (self inputUpTo: $: errorMessage: ' no terminating ":"'). + negated:: false. + spec first = $^ ifTrue: + [negated:: true. + spec:: spec copyFrom: 2 to: spec size]. + ^RxsMessagePredicate selector: spec asSymbol negated: negated +) + +) : ( +'as yet unclassified' +initialize = ( + initializeExceptions. + initializeBackslashConstants. + initializeBackslashSpecials. +) + +initializeBackslashConstants = ( + + (BackslashConstants:: Dictionary new). + BackslashConstants + at: $e put: Character escape; + at: $n put: Character lf; + at: $r put: Character cr; + at: $f put: Character newPage; + at: $t put: Character tab +) + +initializeBackslashSpecials = ( + "Keys are characters that normally follow a \, the values are + associations of classes and initialization selectors on the instance side + of the classes." + "self initializeBackslashSpecials" + + (BackslashSpecials:: Dictionary new). + BackslashSpecials + at: $w put: (Association key: RxsPredicate value: #beWordConstituent); + at: $W put: (Association key: RxsPredicate value: #beNotWordConstituent); + at: $s put: (Association key: RxsPredicate value: #beSpace); + at: $S put: (Association key: RxsPredicate value: #beNotSpace); + at: $d put: (Association key: RxsPredicate value: #beDigit); + at: $D put: (Association key: RxsPredicate value: #beNotDigit); + at: $b put: (Association key: RxsContextCondition value: #beWordBoundary); + at: $B put: (Association key: RxsContextCondition value: #beNonWordBoundary); + at: $< put: (Association key: RxsContextCondition value: #beBeginningOfWord); + at: $> put: (Association key: RxsContextCondition value: #beEndOfWord) +) + +initializeExceptions = ( + "self initializeExceptions" + +" I'm not sure how to port this: + + | parentSignal | + ExceptionObjects := (Dictionary new: 4). + ExceptionObjects + at: #regexErrorSignal + put: (parentSignal := Object errorSignal newSignal + notifierString: 'Regex error - '; + nameClass: self message: #regexErrorSignal); + + at: #syntaxErrorSignal + put: (parentSignal newSignal + notifierString: 'Regex syntax error - '; + nameClass: self message: #syntaxErrorSignal); + + at: #compilationErrorSignal + put: (parentSignal newSignal + notifierString: 'Regex compilation error - '; + nameClass: self message: #compilationErrorSignal); + + at: #matchErrorSignal + put: (parentSignal newSignal + notifierString: 'Regex matching error - '; + nameClass: self message: #matchErrorSignal)" +) + +'documentation' +a:_ introduction:__ = ( +" +A regular expression is a template specifying a class of strings. A +regular expression matcher is an tool that determines whether a string +belongs to a class specified by a regular expression. This is a +common task of a user input validation code, and the use of regular +expressions can GREATLY simplify and speed up development of such +code. As an example, here is how to verify that a string is a valid +hexadecimal number in Smalltalk notation, using this matcher package: + + aString matchesRegex: '16r[[:xdigit:]]+' + +(Coding the same ``the hard way'' is an exercise to a curious reader). + +This matcher is offered to the Smalltalk community in hope it will be +useful. It is free in terms of money, and to a large extent--in terms +of rights of use. Refer to `Boring Stuff' section for legalese. + +The 'What's new in this release' section describes the functionality +introduced in 1.1 release. + +The `Syntax' section explains the recognized syntax of regular +expressions. + +The `Usage' section explains matcher capabilities that go beyond what +String>>matchesRegex: method offers. + +The `Implementation notes' sections says a few words about what is +under the hood. + +Happy hacking, + +--Vassili Bykov + + +August 6, 1996 +April 4, 1999 +" + + self error: 'comment only' +) + +b:_ whatsNewInThisRelease: __ = ( +" +VERSION 1.2.3 (November 2007) + +1. Regexs with ^ or $ applied to copy empty strings caused infinite loops, e.g. ('' copyWithRegex: '^.*$' matchesReplacedWith: 'foo'). Applied a similar correction to that from version 1.1c, to #copyStream:to:(replacingMatchesWith:|translatingMatchesUsing:). +2. Extended RxParser testing to run each test for #copy:translatingMatchesUsing: as well as #search:. +3. Corrected #testSuite test that a dot does not match a null, which was passing by luck with Smalltalk code in a literal array. +4. Added test to end of test suite for fix 1 above. + +VERSION 1.2.2 (November 2006) + +There was no way to specify a backslash in a character set. Now [\\] is accepted. + +VERSION 1.2.1 (August 2006) + +1. Support for returning all ranges (startIndex to: stopIndex) matching a regex - #allRangesOfRegexMatches:, #matchingRangesIn: +2. Added hint to usage documentation on how to get more information about matches when enumerating +3. Syntax description of dot corrected: matches anything but NUL since 1.1a + +VERSION 1.2 (May 2006) + +Fixed case-insensitive search for character sets. + +VERSION 1.1c (December 2004) + +Fixed the issue with #matchesOnStream:do: which caused infinite loops for matches +that matched empty strings. + +VERSION 1.1b (November 2001) + +Changes valueNowOrOnUnwindDo: to ensure:, plus incorporates some earlier fixes. + +VERSION 1.1a (May 2001) + +1. Support for keeping track of multiple subexpressions. +2. Dot (.) matches anything but NUL character, as it should per POSIX spec. +3. Some bug fixes. + +VERSION 1.1 (October 1999) + +Regular expression syntax corrections and enhancements: + +1. Backslash escapes similar to those in Perl are allowed in patterns: + + \w any word constituent character (equivalent to [a-zA-Z0-9_]) + \W any character but a word constituent (equivalent to [^a-xA-Z0-9_] + \d a digit (same as [0-9]) + \D anything but a digit + \s a whitespace character + \S anything but a whitespace character + \b an empty string at a word boundary + \B an empty string not at a word boundary + \< an empty string at the beginning of a word + \> an empty string at the end of a word + +For example, '\w+' is now a valid expression matching any word. + +2. The following backslash escapes are also allowed in character sets +(between square brackets): + + \w, \W, \d, \D, \s, and \S. + +3. The following grep(1)-compatible named character classes are +recognized in character sets as well: + + [:alnum:] + [:alpha:] + [:cntrl:] + [:digit:] + [:graph:] + [:lower:] + [:print:] + [:punct:] + [:space:] + [:upper:] + [:xdigit:] + +For example, the following patterns are equivalent: + + '[[:alnum:]]+' '\w+' '[\w]+' '[a-zA-Z0-9_]+' + +4. Some non-printable characters can be represented in regular +expressions using a common backslash notation: + + \t tab (Character tab) + \n newline (Character lf) + \r carriage return (Character cr) + \f form feed (Character newPage) + \e escape (Character esc) + +5. A dot is corectly interpreted as 'any character but a newline' +instead of 'anything but whitespace'. + +6. Case-insensitive matching. The easiest access to it are new +messages CharacterArray understands: #asRegexIgnoringCase, +#matchesRegexIgnoringCase:, #prefixMatchesRegexIgnoringCase:. + +7. The matcher (an instance of RxMatcher, the result of +String>>asRegex) now provides a collection-like interface to matches +in a particular string or on a particular stream, as well as +substitution protocol. The interface includes the following messages: + + matchesIn: aString + matchesIn: aString collect: aBlock + matchesIn: aString do: aBlock + + matchesOnStream: aStream + matchesOnStream: aStream collect: aBlock + matchesOnStream: aStream do: aBlock + + copy: aString translatingMatchesUsing: aBlock + copy: aString replacingMatchesWith: replacementString + + copyStream: aStream to: writeStream translatingMatchesUsing: aBlock + copyStream: aStream to: writeStream replacingMatchesWith: aString + +Examples: + + '\w+' asRegex matchesIn: 'now is the time' + +returns an OrderedCollection containing four strings: 'now', 'is', +'the', and 'time'. + + '\= 32. + [:lower:] any lowercase character + [:print:] any printable character. In this version, this is the same as [:cntrl:] + [:punct:] any punctuation character. + [:space:] any whitespace character. + [:upper:] any uppercase character. + [:xdigit:] any hexadecimal character. + +Note that these elements are components of the character classes, +i.e. they have to be enclosed in an extra set of square brackets to +form a valid regular expression. For example, a non-empty string of +digits would be represented as '[[:digit:]]+'. + +The above primitive expressions and operators are common to many +implementations of regular expressions. The next primitive expression +is unique to this Smalltalk implementation. + +A sequence of characters between colons is treated as a unary selector +which is supposed to be understood by Characters. A character matches +such an expression if it answers true to a message with that +selector. This allows a more readable and efficient way of specifying +character classes. For example, `[0-9]' is equivalent to `:isDigit:', +but the latter is more efficient. Analogously to character sets, +character classes can be negated: `:^isDigit:' matches a Character +that answers false to #isDigit, and is therefore equivalent to +`[^0-9]'. + +As an example, so far we have seen the following equivalent ways to +write a regular expression that matches a non-empty string of digits: + + '[0-9]+' + '\d+' + '[\d]+' + '[[:digit::]+' + :isDigit:+' + +The last group of special primitive expressions includes: + + . matching any character except a NULL; + ^ matching an empty string at the beginning of a line; + $ matching an empty string at the end of a line. + \b an empty string at a word boundary + \B an empty string not at a word boundary + \< an empty string at the beginning of a word + \> an empty string at the end of a word + + 'axyzb' matchesRegex: 'a.+b' -- true + 'ax zb' matchesRegex: 'a.+b' -- true (space is matched by `.') + 'ax +zb' matchesRegex: 'a.+b' -- true (carriage return is matched by `.') + +Again, the dot ., caret ^ and dollar $ characters are special and should be quoted +to be matched literally. + + EXAMPLES + +As the introductions said, a great use for regular expressions is user +input validation. Following are a few examples of regular expressions +that might be handy in checking input entered by the user in an input +field. Try them out by entering something between the quotes and +print-iting. (Also, try to imagine Smalltalk code that each validation +would require if coded by hand). Most example expressions could have +been written in alternative ways. + +Checking if aString may represent a nonnegative integer number: + + '' matchesRegex: ':isDigit:+' +or + '' matchesRegex: '[0-9]+' +or + '' matchesRegex: '\d+' + +Checking if aString may represent an integer number with an optional +sign in front: + + '' matchesRegex: '(\+|-)?\d+' + +Checking if aString is a fixed-point number, with at least one digit +is required after a dot: + + '' matchesRegex: '(\+|-)?\d+(\.\d+)?' + +The same, but allow notation like `123.': + + '' matchesRegex: '(\+|-)?\d+(\.\d*)?' + +Recognizer for a string that might be a name: one word with first +capital letter, no blanks, no digits. More traditional: + + '' matchesRegex: '[A-Z][A-Za-z]*' + +more Smalltalkish: + + '' matchesRegex: ':isUppercase::isAlphabetic:*' + +A date in format MMM DD, YYYY with any number of spaces in between, in +XX century: + + '' matchesRegex: '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ ]+(\d\d?)[ ]*,[ ]*19(\d\d)' + +Note parentheses around some components of the expression above. As +`Usage' section shows, they will allow us to obtain the actual strings +that have matched them (i.e. month name, day number, and year number). + +For dessert, coming back to numbers: here is a recognizer for a +general number format: anything like 999, or 999.999, or -999.999e+21. + + '' matchesRegex: '(\+|-)?\d+(\.\d*)?((e|E)(\+|-)?\d+)?' + +" + + self error: 'comment only' +) + +d:_ usage:__ = ( +" +The preceding section covered the syntax of regular expressions. It +used the simplest possible interface to the matcher: sending +#matchesRegex: message to the sample string, with regular expression +string as the argument. This section explains hairier ways of using +the matcher. + + PREFIX MATCHING AND CASE-INSENSITIVE MATCHING + +A CharacterArray (an EsString in VA) also understands these messages: + + #prefixMatchesRegex: regexString + #matchesRegexIgnoringCase: regexString + #prefixMatchesRegexIgnoringCase: regexString + +#prefixMatchesRegex: is just like #matchesRegex, except that the whole +receiver is not expected to match the regular expression passed as the +argument; matching just a prefix of it is enough. For example: + + 'abcde' matchesRegex: '(a|b)+' -- false + 'abcde' prefixMatchesRegex: '(a|b)+' -- true + +The last two messages are case-insensitive versions of matching. + + ENUMERATION INTERFACE + +An application can be interested in all matches of a certain regular +expression within a String. The matches are accessible using a +protocol modelled after the familiar Collection-like enumeration +protocol: + + #regex: regexString matchesDo: aBlock + +Evaluates a one-argument for every match of the regular +expression within the receiver string. + + #regex: regexString matchesCollect: aBlock + +Evaluates a one-argument for every match of the regular +expression within the receiver string. Collects results of evaluations +and anwers them as a SequenceableCollection. + + #allRegexMatches: regexString + +Returns a collection of all matches (substrings of the receiver +string) of the regular expression. It is an equivalent of . + + #allRangesOfRegexMatches: regexString + +Returns a collection of all character ranges (startIndex to: stopIndex) +that match the regular expression. + + REPLACEMENT AND TRANSLATION + +It is possible to replace all matches of a regular expression with a +certain string using the message: + + #copyWithRegex: regexString matchesReplacedWith: aString + +For example: + + 'ab cd ab' copyWithRegex: '(a|b)+' matchesReplacedWith: 'foo' + +A more general substitution is match translation: + + #copyWithRegex: regexString matchesTranslatedUsing: aBlock + +This message evaluates a block passing it each match of the regular +expression in the receiver string and answers a copy of the receiver +with the block results spliced into it in place of the respective +matches. For example: + + 'ab cd ab' copyWithRegex: '(a|b)+' matchesTranslatedUsing: [:each | each asUppercase] + +All messages of enumeration and replacement protocols perform a +case-sensitive match. Case-insensitive versions are not provided as +part of a CharacterArray protocol. Instead, they are accessible using +the lower-level matching interface. + + LOWER-LEVEL INTERFACE + +Internally, #matchesRegex: works as follows: + +1. A fresh instance of RxParser is created, and the regular expression +string is passed to it, yielding the expression's syntax tree. + +2. The syntax tree is passed as an initialization parameter to an +instance of RxMatcher. The instance sets up some data structure that +will work as a recognizer for the regular expression described by the +tree. + +3. The original string is passed to the matcher, and the matcher +checks for a match. + + THE MATCHER + +If you repeatedly match a number of strings against the same regular +expression using one of the messages defined in CharacterArray, the +regular expression string is parsed and a matcher is created anew for +every match. You can avoid this overhead by building a matcher for +the regular expression, and then reusing the matcher over and over +again. You can, for example, create a matcher at a class or instance +initialization stage, and store it in a variable for future use. + +You can create a matcher using one of the following methods: + + - Sending #forString:ignoreCase: message to RxMatcher class, with +the regular expression string and a Boolean indicating whether case is +ignored as arguments. + + - Sending #forString: message. It is equivalent to <... forString: +regexString ignoreCase: false>. + +A more convenient way is using one of the two matcher-created messages +understood by CharacterArray. + + - is equivalent to . + + - is equivalent to . + +Here are four examples of creating a matcher: + + hexRecognizer := RxMatcher forString: '16r[0-9A-Fa-f]+' + hexRecognizer := RxMatcher forString: '16r[0-9A-Fa-f]+' ignoreCase: false + hexRecognizer := '16r[0-9A-Fa-f]+' asRegex + hexRecognizer := '16r[0-9A-F]+' asRegexIgnoringCase + + MATCHING + +The matcher understands these messages (all of them return true to +indicate successful match or search, and false otherwise): + +matches: aString + + True if the whole target string (aString) matches. + +matchesPrefix: aString + + True if some prefix of the string (not necessarily the whole + string) matches. + +search: aString + + Search the string for the first occurrence of a matching + substring. (Note that the first two methods only try matching from + the very beginning of the string). Using the above example with a + matcher for `a+', this method would answer success given a string + `baaa', while the previous two would fail. + +matchesStream: aStream +matchesStreamPrefix: aStream +searchStream: aStream + + Respective analogs of the first three methods, taking input from a + stream instead of a string. The stream must be positionable and + peekable. + +All these methods answer a boolean indicating success. The matcher +also stores the outcome of the last match attempt and can report it: + +lastResult + + Answers a Boolean -- the outcome of the most recent match + attempt. If no matches were attempted, the answer is unspecified. + + SUBEXPRESSION MATCHES + +After a successful match attempt, you can query the specifics of which +part of the original string has matched which part of the whole +expression. + +A subexpression is a parenthesized part of a regular expression, or +the whole expression. When a regular expression is compiled, its +subexpressions are assigned indices starting from 1, depth-first, +left-to-right. For example, `((ab)+(c|d))?ef' includes the following +subexpressions with these indices: + + 1: ((ab)+(c|d))?ef + 2: (ab)+(c|d) + 3: ab + 4: c|d + +After a successful match, the matcher can report what part of the +original string matched what subexpression. It understandards these +messages: + +subexpressionCount + + Answers the total number of subexpressions: the highest value that + can be used as a subexpression index with this matcher. This value + is available immediately after initialization and never changes. + +subexpression: anIndex + + An index must be a valid subexpression index, and this message + must be sent only after a successful match attempt. The method + answers a substring of the original string the corresponding + subexpression has matched to. + +subBeginning: anIndex +subEnd: anIndex + + Answer positions within the original string or stream where the + match of a subexpression with the given index has started and + ended, respectively. + +This facility provides a convenient way of extracting parts of input +strings of complex format. For example, the following piece of code +uses the 'MMM DD, YYYY' date format recognizer example from the +`Syntax' section to convert a date to a three-element array with year, +month, and day strings (you can select and evaluate it right here): + + | matcher | + matcher := RxMatcher forString: '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ ]+(:isDigit::isDigit:?)[ ]*,[ ]*(19|20)(:isDigit::isDigit:)'. + (matcher matches: 'Aug 6, 1996') + ifTrue: + [Array + with: (matcher subexpression: 5) + with: (matcher subexpression: 2) + with: (matcher subexpression: 3)] + ifFalse: ['no match'] + +(should answer ` #('96' 'Aug' '6')'). + + ENUMERATION AND REPLACEMENT + +The enumeration and replacement protocols exposed in CharacterArray +are actually implemented by the matcher. The following messages are +understood: + + #matchesIn: aString + #matchesIn: aString do: aBlock + #matchesIn: aString collect: aBlock + #copy: aString replacingMatchesWith: replacementString + #copy: aString translatingMatchesUsing: aBlock + #matchingRangesIn: aString + + #matchesOnStream: aStream + #matchesOnStream: aStream do: aBlock + #matchesOnStream: aStream collect: aBlock + #copy: sourceStream to: targetStream replacingMatchesWith: replacementString + #copy: sourceStream to: targetStream translatingMatchesWith: aBlock + +Note that in those methods that take a block, the block may refer to the rxMatcher itself, +e.g. to collect information about the position the match occurred at, or the +subexpressions of the match. An example can be seen in #matchingRangesIn: + + ERROR HANDLING + +Exception signaling objects (Signals in VisualWorks, Exceptions in VisualAge) are +accessible through RxParser class protocol. To handle possible errors, use +the protocol described below to obtain the exception objects and use the +protocol of the native Smalltalk implementation to handle them. + +If a syntax error is detected while parsing expression, +RxParser>>syntaxErrorSignal is raised/signaled. + +If an error is detected while building a matcher, +RxParser>>compilationErrorSignal is raised/signaled. + +If an error is detected while matching (for example, if a bad selector +was specified using `::' syntax, or because of the matcher's +internal error), RxParser>>matchErrorSignal is raised + +RxParser>>regexErrorSignal is the parent of all three. Since any of +the three signals can be raised within a call to #matchesRegex:, it is +handy if you want to catch them all. For example: + +VisualWorks: + + RxParser regexErrorSignal + handle: [:ex | ex returnWith: nil] + do: ['abc' matchesRegex: '))garbage['] + +VisualAge: + + ['abc' matchesRegex: '))garbage['] + when: RxParser regexErrorSignal + do: [:signal | signal exitWith: nil] + +" + + self error: 'comment only' +) + +e:_ implementationNotes:__ = ( +" + Version: 1.1 + Released: October 1999 + Mail to: Vassili Bykov , + Flames to: /dev/null + + WHAT IS ADDED + +The matcher includes classes in two categories: + VB-Regex-Syntax + VB-Regex-Matcher +and a few CharacterArray methods in `VB-regex' protocol. No system +classes or methods are modified. + + WHAT TO LOOK AT FIRST + +String>>matchesRegex: -- in 90% cases this method is all you need to +access the package. + +RxParser -- accepts a string or a stream of characters with a regular +expression, and produces a syntax tree corresponding to the +expression. The tree is made of instances of Rxs classes. + +RxMatcher -- accepts a syntax tree of a regular expression built by +the parser and compiles it into a matcher: a structure made of +instances of Rxm classes. The RxMatcher instance can test +whether a string or a positionable stream of characters matches the +original regular expression, or search a string or a stream for +substrings matching the expression. After a match is found, the +matcher can report a specific string that matched the whole +expression, or any parenthesized subexpression of it. + +All other classes support the above functionality and are used by +RxParser, RxMatcher, or both. + + CAVEATS + +The matcher is similar in spirit, but NOT in the design--let alone the +code--to the original Henry Spencer's regular expression +implementation in C. The focus is on simplicity, not on efficiency. +I didn't optimize or profile anything. I may in future--or I may not: +I do this in my spare time and I don't promise anything. + +The matcher passes H. Spencer's test suite (see 'test suite' +protocol), with quite a few extra tests added, so chances are good +there are not too many bugs. But watch out anyway. + + EXTENSIONS, FUTURE, ETC. + +With the existing separation between the parser, the syntax tree, and +the matcher, it is easy to extend the system with other matchers based +on other algorithms. In fact, I have a DFA-based matcher right now, +but I don't feel it is good enough to include it here. I might add +automata-based matchers later, but again I don't promise anything. + + HOW TO REACH ME + +As of today (December 20, 2000), you can contact me at +. If this doesn't work, look around +comp.lang.smalltalk or comp.lang.lisp. +" + + self error: 'comment only' +) + +f:_ boringStuff: __ = ( +" +The Regular Expression Matcher (``The Software'') +is Copyright (C) 1996, 1999 Vassili Bykov. +It is provided to the Smalltalk community in hope it will be useful. + +1. This license applies to the package as a whole, as well as to any + component of it. By performing any of the activities described + below, you accept the terms of this agreement. + +2. The software is provided free of charge, and ``as is'', in hope + that it will be useful, with ABSOLUTELY NO WARRANTY. The entire + risk and all responsibility for the use of the software is with + you. Under no circumstances the author may be held responsible for + loss of data, loss of profit, or any other damage resulting + directly or indirectly from the use of the software, even if the + damage is caused by defects in the software. + +3. You may use this software in any applications you build. + +4. You may distribute this software provided that the software + documentation and copyright notices are included and intact. + +5. You may create and distribute modified versions of the software, + such as ports to other Smalltalk dialects or derived work, provided + that: + + a. any modified version is expressly marked as such and is not + misrepresented as the original software; + + b. credit is given to the original software in the source code and + documentation of the derived work; + + c. the copyright notice at the top of this document accompanies + copyright notices of any modified version. " + + self error: 'comment only' +) + +'exception signaling' +doHandlingMessageNotUnderstood: aBlock = ( + "MNU should be trapped and resignaled as a match error in a few places in the matcher. + This method factors out this dialect-dependent code to make porting easier." + + "^Object messageNotUnderstoodSignal + handle: + [:ex | ex restartDo: + [RxParser signalMatchException: 'invalid predicate selector']] + do: aBlock" + +" ^[aBlock value] on: Exception do: [:ex | ex return: false]" + + ^aBlock on: MessageNotUnderstood do: [:ex | MatchError signal: 'invalid predicate selector'] + +) + +signalCompilationException: errorString = ( + ^self compilationErrorSignal raiseErrorString: errorString +) + +signalMatchException: errorString = ( + ^self matchErrorSignal raiseErrorString: errorString +) + +signalSyntaxException: errorString = ( + ^self syntaxErrorSignal raiseErrorString: errorString +) + +'preferences' +preferredMatcherClass = ( + "The matcher to use. For now just one is available, but in + principle this determines the matchers built implicitly, + such as by String>>asRegex, or String>>matchesRegex:. + This might seem a bit strange place for this preference, but + Parser is still more or less `central' thing in the whole package." + + ^RxMatcher +) + +'test suite' +compileRegex: regexSource into: matcherClass = ( + "Compile the regex and answer the matcher, or answer nil if compilation fails." + + | syntaxTree | + syntaxTree:: self safelyParse: regexSource. + syntaxTree == nil ifTrue: [^nil]. + ^matcherClass for: syntaxTree +) + +runProtocolTestsForMatcher: matcherClass = ( + | matcher | + Transcript show: 'Testing matcher protocol...'. + matcher:: matcherClass forString: '\w+'. + (matcher matchesIn: 'now is the time') asArray = {'now'. 'is'. 'the'. 'time'} + ifFalse: [self error: 'matchesIn: test failed']. + (matcher copy: 'now is the time ' translatingMatchesUsing: [:s | s reverse]) + = 'won si eht emit ' + ifFalse: [self error: 'copy:translatingMatchesWith: test failed']. + "See that the match context is preserved while copying stuff between matches:" + ((matcherClass forString: '\<\d\D+') + copy: '9aaa1bbb 8ccc' + replacingMatchesWith: 'foo') = 'foo1bbb foo' + ifFalse: [self error: 'test failed']. + Transcript show: 'OK'; cr +) + +runRegexTestsForMatcher: matcherClass = ( + "Run the whole suite of tests for the given matcher class. May blow up + if anything goes wrong with the matcher or parser. Since this is a + developer's tool, who cares?" + "self runRegexTestsForMatcher: RxMatcher" + + | failures | + failures:: 0. + Transcript cr. + self testSuite do: [:clause | + | rxSource matcher isOK | + rxSource:: clause first. + Transcript show: 'Testing regex: '; show: rxSource printString; cr. + matcher:: self compileRegex: rxSource into: matcherClass. + matcher == nil + ifTrue: + [(clause at: 2) isNil + ifTrue: + [Transcript tab; show: 'Compilation error as expected (ok)'; cr] + ifFalse: + [Transcript tab; + show: 'Compilation error, UNEXPECTED -- FAILED'; cr. + failures:: failures + 1]] + ifFalse: + [(clause at: 2) == nil + ifTrue: + [Transcript tab; + show: 'Compilation succeeded, should have failed -- FAILED!'; + cr. + failures:: failures + 1] + ifFalse: + [2 to: clause size by: 3 do: + [:i | + isOK:: self + test: matcher + with: (clause at: i) + expect: (clause at: i + 1) + withSubexpressions: (clause at: i + 2). + isOK ifFalse: [failures:: failures + 1]. + Transcript + show: (isOK ifTrue: [' (ok).'] ifFalse: [' -- FAILED!']); + cr]]]]. + failures = 0 + ifTrue: [Transcript show: 'PASSED ALL TESTS.'; cr] + ifFalse: [Transcript show: failures printString, ' TESTS FAILED!'; cr] +) + +runTestsForMatcher: matcherClass = ( + "Run the whole suite of tests for the given matcher class. May blow up + if something goes wrong with the matcher or the parser. Since this is a + developer's tool, who cares?" + "self runTestsForMatcher: RxMatcher" + + self + runRegexTestsForMatcher: matcherClass; + runProtocolTestsForMatcher: matcherClass +) + +test: aMatcher with: testString expect: expected withSubexpressions: subexpr = ( + + | copy got | + Transcript tab; + show: 'Matching: '; + show: testString printString. + copy:: aMatcher copy: testString translatingMatchesUsing: [:s | s]. + copy ~= testString ifTrue: + [Transcript show: ' (copy failed: "', copy, '")'. + ^false]. + Transcript + show: ' expected: '; + show: expected printString; + show: ' got: '. + got:: aMatcher search: testString. + Transcript show: got printString. + got ~= expected + ifTrue: [^false]. + (subexpr notNil and: [aMatcher supportsSubexpressions]) + ifFalse: + [^true] + ifTrue: + [ | isOK | + isOK:: true. + 1 to: subexpr size by: 2 do: [: i | + | sub subExpect subGot | + sub:: subexpr at: i. + subExpect:: subexpr at: i + 1. + subGot:: aMatcher subexpression: sub. + Transcript cr; tab; tab; + show: 'Subexpression: ', sub printString; + show: ' expected: '; + show: subExpect printString; + show: ' got: '; + show: subGot printString. + subExpect ~= subGot + ifTrue: + [Transcript show: ' -- MISMATCH'. + isOK:: false]]. + ^isOK] +) + +'utilities' +parse: aString = ( + "Parse the argument and return the result (the parse tree). + In case of a syntax error, the corresponding exception is signaled." + + ^self new parse: aString +) + +safelyParse: aString = ( + "Parse the argument and return the result (the parse tree). + In case of a syntax error, return nil. + Exception handling here is dialect-dependent." + + ^self syntaxErrorSignal + handle: [:ex | ex returnWith: nil] + do: [self new parse: aString] +) + +) + +class RxsCharSet elements: aCollection negated: aBoolean = RxsNode ( +" +A character set corresponds to a [...] construct in the regular expression. + +Instance variables: + elements An element can be one of: RxsCharacter, RxsRange, or RxsPredicate. + negated " +| + negated::= aBoolean. + elements::= aCollection. +| +) +('accessing' +dispatchTo: aMatcher = ( + "Inform the matcher of the kind of the node, and it + will do whatever it has to." + + ^aMatcher syntaxCharSet: self +) + +hasPredicates = ( + + ^elements contains: [:some | some isEnumerable not] +) + +predicateIgnoringCase: aBoolean = ( + + | predicate enumerable | + enumerable:: self enumerablePartPredicateIgnoringCase: aBoolean. + ^self hasPredicates + ifFalse: [enumerable] + ifTrue: + [predicate:: self predicatePartPredicate. + negated + ifTrue: [[:char | (enumerable value: char) and: [predicate value: char]]] + ifFalse: [[:char | (enumerable value: char) or: [predicate value: char]]]] +) + +predicates = ( + + ^(elements reject: [:some | some isEnumerable]) + collect: [:each | each predicate] +) + +'privileged' +enumerablePartPredicateIgnoringCase: aBoolean = ( + + | enumeration | + enumeration:: self optimalSetIgnoringCase: aBoolean. + ^negated + ifTrue: [[:char | (enumeration includes: char) not]] + ifFalse: [[:char | enumeration includes: char]] +) + +enumerableSetIgnoringCase: aBoolean = ( + "Answer a collection of characters that make up the portion of me + that can be enumerated." + + | set | + set:: Set new. + elements do: + [:each | + each isEnumerable ifTrue: + [each enumerateTo: set ignoringCase: aBoolean]]. + ^set +) + +optimalSetIgnoringCase: aBoolean = ( + "Assuming the client with search the `set' using #includes:, + answer a collection with the contents of `set', of the class + that will provide the fastest lookup. Strings are faster than + Sets for short strings." + + | set | + set:: self enumerableSetIgnoringCase: aBoolean. + "fails: quirk btwn VW and Sq? + ^set size < 10 + ifTrue: [String withAll: set] + ifFalse: [set]" + ^set +) + +predicatePartPredicate = ( + "Answer a predicate that tests all of my elements that cannot be + enumerated." + + | predicates | + predicates:: elements reject: [:some | some isEnumerable]. + predicates isEmpty + ifTrue: [^[:char | negated]]. + predicates size = 1 + ifTrue: [^negated + ifTrue: [predicates first predicateNegation] + ifFalse: [predicates first predicate]]. + predicates:: predicates collect: [:each | each predicate]. + ^negated + ifFalse: + [[:char | predicates contains: [:some | some value: char]]] + ifTrue: + [[:char | (predicates contains: [:some | some value: char]) not]] +) + +'testing' +isEnumerable = ( + + elements detect: [:some | some isEnumerable not] ifNone: [^true]. + ^false +) + +isNegated = ( + + ^negated +) + +) + +class MatchError = RegexError ("Regex matching error") +() + +class RxmPredicate = RxmLink ( +"Instance holds onto a one-argument block and matches exactly one character if the block evaluates to true when passed the character as the argument. + +Instance variables: + predicate " +| + predicateS +| +) +('initialize-release' +bePerform: aSelector = ( + "Match any single character that answers true to this message." + + self predicate: + [:char | + RxParser doHandlingMessageNotUnderstood: [char perform: aSelector]] +) + +bePerformNot: aSelector = ( + "Match any single character that answers false to this message." + + self predicate: + [:char | + RxParser doHandlingMessageNotUnderstood: [(char perform: aSelector) not]] +) + +predicate: aBlock = ( + "This link will match any single character for which + evaluates to true." + + aBlock numArgs ~= 1 ifTrue: [self error: 'bad predicate block']. + predicateS:: aBlock. + ^self +) + +'accessing' +predicate = ( ^predicateS) + +'matching' +matchAgainst: aMatcher = ( + "Match if the predicate block evaluates to true when given the + current stream character as the argument." + + | original | + original:: aMatcher currentState. + (aMatcher atEnd not + and: [(predicate value: aMatcher next) + and: [next matchAgainst: aMatcher]]) + ifTrue: [^true] + ifFalse: + [aMatcher restoreState: original. + ^false] +) + +) : ( +'as yet unclassified' +with: unaryBlock = ( + ^self new predicate: unaryBlock +) + +) + +class RxsRegex branch: b regex: r = RxsNode( +"The body of a parenthesized thing, or a top-level expression, also an atom. + +Instance variables: + branch + regex " +| + branch ::= b. + regex ::= r. +| +) +('accessing' +dispatchTo: aMatcher = ( + "Inform the matcher of the kind of the node, and it + will do whatever it has to." + + ^aMatcher syntaxRegex: self +) + +'testing' +isNullable = ( + + ^branch isNullable or: [regex notNil and: [regex isNullable]] +) + +) + +class RxmSubstring = RxmLink ( +"Instance holds onto a string and matches exactly this string, and exactly once." +| + sample compare +| + self beCaseSensitive. +) +('initialize-release' +beCaseInsensitive = ( + compare:: [:char1 :char2 | char1 sameAs: char2] +) + +beCaseSensitive = ( + compare:: [:char1 :char2 | char1 = char2] +) + +character: aCharacter ignoreCase: aBoolean = ( + "Match exactly this character." + + sample:: String with: aCharacter. + aBoolean ifTrue: [self beCaseInsensitive] +) + +substring: aString ignoreCase: aBoolean = ( + "Match exactly this string." + + sample:: aString. + aBoolean ifTrue: [self beCaseInsensitive] +) + +'matching' +matchAgainst: aMatcher = ( + "Match if my sample stream is exactly the current prefix + of the matcher stream's contents." + + | originalState sampleStream mismatch | + originalState:: aMatcher currentState. + sampleStream:: self sampleStream. + mismatch:: false. + [sampleStream atEnd + or: [aMatcher atEnd + or: [mismatch:: (compare value: sampleStream next value: aMatcher next) not]]] whileFalse. + (mismatch not and: [sampleStream atEnd and: [next matchAgainst: aMatcher]]) + ifTrue: [^true] + ifFalse: + [aMatcher restoreState: originalState. + ^false] +) + +'private' +sampleStream = ( + ^sample readStream +) + +) + +class RxmTerminator = ( +"Instances of this class are used to terminate matcher's chains. When a match reaches this (an instance receives #matchAgainst: message), the match is considered to succeed. Instances also support building protocol of RxmLinks, with some restrictions." +|| +) +('matching' +matchAgainst: aStream = ( + "If got here, the match is successful." + ^true +) + +'building' +pointTailTo: anRxmLink = ( + "Branch tails are never redirected by the build algorithm. + Healthy terminators should never receive this." + + CompilationError signal: + 'internal matcher build error - redirecting terminator tail' +) + +terminateWith: aTerminator = ( + "Branch terminators are never supposed to change. + Make sure this is the case." + + aTerminator ~~ self + ifTrue: [CompilationError signal: + 'internal matcher build error - wrong terminator'] +) + +) + +class RxmLink = ( +"A matcher is built of a number of links interconnected into some intricate structure. Regardless of fancy stuff, any link (except for the terminator) has the next one. Any link can match against a stream of characters, recursively propagating the match to the next link. Any link supports a number of matcher-building messages. This superclass does all of the above. + +The class is not necessarily abstract. It may double as an empty string matcher: it recursively propagates the match to the next link, thus always matching nothing successfully. + +Principal method: + matchAgainst: aMatcher + Any subclass will reimplement this to test the state of the matcher, most + probably reading one or more characters from the matcher's stream, and + either decide it has matched and answer true, leaving matcher stream + positioned at the end of match, or answer false and restore the matcher + stream position to whatever it was before the matching attempt. + +Instance variables: + next The next link in the structure." +| + next +| +) +('building' +pointTailTo: anRxmLink = ( + "Propagate this message along the chain of links. + Point `next' reference of the last link to . + If the chain is already terminated, blow up." + + next == nil + ifTrue: [next:: anRxmLink] + ifFalse: [next pointTailTo: anRxmLink] +) + +terminateWith: aTerminator = ( + "Propagate this message along the chain of links, and + make aTerminator the `next' link of the last link in the chain. + If the chain is already reminated with the same terminator, + do not blow up." + + next == nil + ifTrue: [next:: aTerminator] + ifFalse: [next terminateWith: aTerminator] +) + +'matching' +matchAgainst: aMatcher = ( + "If a link does not match the contents of the matcher's stream, + answer false. Otherwise, let the next matcher in the chain match." + + ^next matchAgainst: aMatcher +) + +) + +class RxmBranch = RxmLink ( +"This is a branch of a matching process. Either `next' chain should match, or `alternative', if not nil, should match. Since this is also used to build loopbacks to match repetitions, `loopback' variable indicates whether the instance is a loopback: it affects the matcher-building operations (which of the paths through the branch is to consider as the primary when we have to find the 'tail' of a matcher construct)." +| + alternative loopback +| + + loopback:: false. +) +('building' +pointTailTo: aNode = ( + "See superclass for explanations." + + loopback + ifTrue: [alternative == nil + ifTrue: [alternative:: aNode] + ifFalse: [alternative pointTailTo: aNode]] + ifFalse: [super pointTailTo: aNode] +) + +terminateWith: aNode = ( + "See superclass for explanations." + + loopback + ifTrue: [alternative == nil + ifTrue: [alternative:: aNode] + ifFalse: [alternative terminateWith: aNode]] + ifFalse: [super terminateWith: aNode] +) + +'initialize-release' +beLoopback = ( + "See class comment for instance variable description." + + loopback:: true +) + +'matching' +matchAgainst: aMatcher = ( + "Match either `next' or `alternative'. Fail if the alternative is nil." + + ^(next matchAgainst: aMatcher) + or: [alternative notNil + and: [alternative matchAgainst: aMatcher]] +) + +) + +class RxsPiece atom: a min: mn max: mx = RxsNode ( +"A piece is an atom, possibly optional or repeated a number of times. + +Instance variables: + atom + min + max nil means infinity" +| + atom ::= a. + min ::= mn. + max ::= mx. +| +) +('accessing' +character = ( + "If this node is atomic, answer the character it + represents. It is the caller's responsibility to make sure this + node is indeed atomic before using this." + + ^atom character +) + +dispatchTo: aMatcher = ( + "Inform the matcher of the kind of the node, and it + will do whatever it has to." + + ^aMatcher syntaxPiece: self +) + +'restricted' +isAtomic = ( + "A piece is atomic if only it contains exactly one atom + which is atomic (sic)." + + ^self isSingular and: [atom isAtomic] +) + +'testing' +isNullable = ( + "A piece is nullable if it allows 0 matches. + This is often handy to know for optimization." + + ^min = 0 or: [atom isNullable] +) + +isOptional = ( + + ^min = 0 and: [max = 1] +) + +isPlus = ( + + ^min = 1 and: [max == nil] +) + +isSingular = ( + "A piece with a range is 1 to 1 needs can be compiled + as a simple match." + + ^min = 1 and: [max = 1] +) + +isStar = ( + ^min = 0 and: [max == nil] +) + +) : ( +'initialize-release' +atom: a = ( + "This piece is exactly one occurrence of the specified RxsAtom." + + ^atom: a min: 1 max: 1 +) + +optionalAtom: a = ( + "This piece is 0 or 1 occurrences of the specified RxsAtom." + + ^atom: a min: 0 max: 1 +) + +plusAtom: a = ( + "This piece is one or more occurrences of the specified RxsAtom." + + ^atom: a min: 1 max: nil +) + +starAtom: anAtom = ( + "This piece is any number of occurrences of the atom." + + ^atom: anAtom min: 0 max: nil +) + +)'as yet unclassified' +asRegex: aCharacterArray = ( + "Compile the receiver as a regex matcher. May raise RxParser>>syntaxErrorSignal + or RxParser>>compilationErrorSignal. + This is a part of the Regular Expression Matcher package, (c) 1996, 1999 Vassili Bykov. + Refer to `documentation' protocol of RxParser class for details." + + ^RxParser preferredMatcherClass for: (RxParser new parse: aCharacterArray) +) + +asRegexIgnoringCase: aCharacterArray = ( + "Compile the receiver as a regex matcher. May raise RxParser>>syntaxErrorSignal + or RxParser>>compilationErrorSignal. + This is a part of the Regular Expression Matcher package, (c) 1996, 1999 Vassili Bykov. + Refer to `documentation' protocol of RxParser class for details." + + ^RxParser preferredMatcherClass + for: (RxParser new parse: aCharacterArray) + ignoreCase: true +) + +string: aCharacterArray allRangesOfRegexMatches: rxString = ( + + ^(asRegex: rxString) matchingRangesIn: aCharacterArray +) + +string: aCharacterArray allRegexMatches: rxString = ( + ^(asRegex: rxString) matchesIn: self +) + +string: aCharacterArray copyWithRegex: rxString matchesReplacedWith: aString = ( + + ^(asRegex: rxString) + copy: aCharacterArray replacingMatchesWith: aString +) + +string: aCharacterArray copyWithRegex: rxString matchesTranslatedUsing: aBlock = ( + + ^(asRegex: rxString) + copy: aCharacterArray translatingMatchesUsing: aBlock +) + +string: aCharacterArray matchesRegex: regexString = ( + "Test if the receiver matches a regex. May raise RxParser>>regexErrorSignal or + child signals. + This is a part of the Regular Expression Matcher package, (c) 1996, 1999 Vassili Bykov. + Refer to `documentation' protocol of RxParser class for details." + + ^(asRegex: regexString) matches: aCharacterArray +) + +string: aCharacterArray matchesRegexIgnoringCase: regexString = ( + "Test if the receiver matches a regex. May raise RxParser>>regexErrorSignal or + child signals. + This is a part of the Regular Expression Matcher package, (c) 1996, 1999 Vassili Bykov. + Refer to `documentation' protocol of RxParser class for details." + + ^(asRegexIgnoringCase: regexString) matches: aCharacterArray +) + +string: aCharacterArray prefixMatchesRegex: regexString = ( + "Test if the receiver's prefix matches a regex. + May raise RxParser class>>regexErrorSignal or child signals. + This is a part of the Regular Expression Matcher package, (c) 1996, 1999 Vassili Bykov. + Refer to `documentation' protocol of RxParser class for details." + + ^(asRegex: regexString) matchesPrefix: aCharacterArray +) + +string: aCharacterArray prefixMatchesRegexIgnoringCase: regexString = ( + "Test if the receiver's prefix matches a regex. + May raise RxParser class>>regexErrorSignal or child signals. + This is a part of the Regular Expression Matcher package, (c) 1996, 1999 Vassili Bykov. + Refer to `documentation' protocol of RxParser class for details." + + ^(asRegexIgnoringCase: regexString) matchesPrefix: aCharacterArray +) + +string: aCharacterArray regex: rxString matchesCollect: aBlock = ( + + ^(asRegex: rxString) matchesIn: aCharacterArray collect: aBlock +) + +string: aCharacterArray regex: rxString matchesDo: aBlock = ( + + ^(asRegex: rxString) matchesIn: aCharacterArray do: aBlock +) + +) \ No newline at end of file diff --git a/tests/examplefiles/SmallCheck.hs b/tests/examplefiles/SmallCheck.hs new file mode 100644 index 0000000..36c39ef --- /dev/null +++ b/tests/examplefiles/SmallCheck.hs @@ -0,0 +1,378 @@ +--------------------------------------------------------------------- +-- SmallCheck: another lightweight testing library. +-- Colin Runciman, August 2006 +-- Version 0.2 (November 2006) +-- +-- After QuickCheck, by Koen Claessen and John Hughes (2000-2004). +--------------------------------------------------------------------- + +module SmallCheck ( + smallCheck, depthCheck, + Property, Testable, + forAll, forAllElem, + exists, existsDeeperBy, thereExists, thereExistsElem, + (==>), + Series, Serial(..), + (\/), (><), two, three, four, + cons0, cons1, cons2, cons3, cons4, + alts0, alts1, alts2, alts3, alts4, + N(..), Nat, Natural, + depth, inc, dec + ) where + +import Data.List (intersperse) +import Control.Monad (when) +import System.IO (stdout, hFlush) + +------------------ ----------------- + +-- Series arguments should be interpreted as a depth bound (>=0) +-- Series results should have finite length + +type Series a = Int -> [a] + +-- sum +infixr 7 \/ +(\/) :: Series a -> Series a -> Series a +s1 \/ s2 = \d -> s1 d ++ s2 d + +-- product +infixr 8 >< +(><) :: Series a -> Series b -> Series (a,b) +s1 >< s2 = \d -> [(x,y) | x <- s1 d, y <- s2 d] + +------------------- ------------------ + +-- enumerated data values should be finite and fully defined +-- enumerated functional values should be total and strict + +-- bounds: +-- for data values, the depth of nested constructor applications +-- for functional values, both the depth of nested case analysis +-- and the depth of results + +class Serial a where + series :: Series a + coseries :: Serial b => Series (a->b) + +instance Serial () where + series _ = [()] + coseries d = [ \() -> b + | b <- series d ] + +instance Serial Int where + series d = [(-d)..d] + coseries d = [ \i -> if i > 0 then f (N (i - 1)) + else if i < 0 then g (N (abs i - 1)) + else z + | z <- alts0 d, f <- alts1 d, g <- alts1 d ] + +instance Serial Integer where + series d = [ toInteger (i :: Int) + | i <- series d ] + coseries d = [ f . (fromInteger :: Integer->Int) + | f <- series d ] + +newtype N a = N a + +instance Show a => Show (N a) where + show (N i) = show i + +instance (Integral a, Serial a) => Serial (N a) where + series d = map N [0..d'] + where + d' = fromInteger (toInteger d) + coseries d = [ \(N i) -> if i > 0 then f (N (i - 1)) + else z + | z <- alts0 d, f <- alts1 d ] + +type Nat = N Int +type Natural = N Integer + +instance Serial Float where + series d = [ encodeFloat sig exp + | (sig,exp) <- series d, + odd sig || sig==0 && exp==0 ] + coseries d = [ f . decodeFloat + | f <- series d ] + +instance Serial Double where + series d = [ frac (x :: Float) + | x <- series d ] + coseries d = [ f . (frac :: Double->Float) + | f <- series d ] + +frac :: (Real a, Fractional a, Real b, Fractional b) => a -> b +frac = fromRational . toRational + +instance Serial Char where + series d = take (d+1) ['a'..'z'] + coseries d = [ \c -> f (N (fromEnum c - fromEnum 'a')) + | f <- series d ] + +instance (Serial a, Serial b) => + Serial (a,b) where + series = series >< series + coseries = map uncurry . coseries + +instance (Serial a, Serial b, Serial c) => + Serial (a,b,c) where + series = \d -> [(a,b,c) | (a,(b,c)) <- series d] + coseries = map uncurry3 . coseries + +instance (Serial a, Serial b, Serial c, Serial d) => + Serial (a,b,c,d) where + series = \d -> [(a,b,c,d) | (a,(b,(c,d))) <- series d] + coseries = map uncurry4 . coseries + +uncurry3 :: (a->b->c->d) -> ((a,b,c)->d) +uncurry3 f (x,y,z) = f x y z + +uncurry4 :: (a->b->c->d->e) -> ((a,b,c,d)->e) +uncurry4 f (w,x,y,z) = f w x y z + +two :: Series a -> Series (a,a) +two s = s >< s + +three :: Series a -> Series (a,a,a) +three s = \d -> [(x,y,z) | (x,(y,z)) <- (s >< s >< s) d] + +four :: Series a -> Series (a,a,a,a) +four s = \d -> [(w,x,y,z) | (w,(x,(y,z))) <- (s >< s >< s >< s) d] + +cons0 :: + a -> Series a +cons0 c _ = [c] + +cons1 :: Serial a => + (a->b) -> Series b +cons1 c d = [c z | d > 0, z <- series (d-1)] + +cons2 :: (Serial a, Serial b) => + (a->b->c) -> Series c +cons2 c d = [c y z | d > 0, (y,z) <- series (d-1)] + +cons3 :: (Serial a, Serial b, Serial c) => + (a->b->c->d) -> Series d +cons3 c d = [c x y z | d > 0, (x,y,z) <- series (d-1)] + +cons4 :: (Serial a, Serial b, Serial c, Serial d) => + (a->b->c->d->e) -> Series e +cons4 c d = [c w x y z | d > 0, (w,x,y,z) <- series (d-1)] + +alts0 :: Serial a => + Series a +alts0 d = series d + +alts1 :: (Serial a, Serial b) => + Series (a->b) +alts1 d = if d > 0 then series (dec d) + else [\_ -> x | x <- series d] + +alts2 :: (Serial a, Serial b, Serial c) => + Series (a->b->c) +alts2 d = if d > 0 then series (dec d) + else [\_ _ -> x | x <- series d] + +alts3 :: (Serial a, Serial b, Serial c, Serial d) => + Series (a->b->c->d) +alts3 d = if d > 0 then series (dec d) + else [\_ _ _ -> x | x <- series d] + +alts4 :: (Serial a, Serial b, Serial c, Serial d, Serial e) => + Series (a->b->c->d->e) +alts4 d = if d > 0 then series (dec d) + else [\_ _ _ _ -> x | x <- series d] + +instance Serial Bool where + series = cons0 True \/ cons0 False + coseries d = [ \x -> if x then b1 else b2 + | (b1,b2) <- series d ] + +instance Serial a => Serial (Maybe a) where + series = cons0 Nothing \/ cons1 Just + coseries d = [ \m -> case m of + Nothing -> z + Just x -> f x + | z <- alts0 d , + f <- alts1 d ] + +instance (Serial a, Serial b) => Serial (Either a b) where + series = cons1 Left \/ cons1 Right + coseries d = [ \e -> case e of + Left x -> f x + Right y -> g y + | f <- alts1 d , + g <- alts1 d ] + +instance Serial a => Serial [a] where + series = cons0 [] \/ cons2 (:) + coseries d = [ \xs -> case xs of + [] -> y + (x:xs') -> f x xs' + | y <- alts0 d , + f <- alts2 d ] + +-- Warning: the coseries instance here may generate duplicates. +instance (Serial a, Serial b) => Serial (a->b) where + series = coseries + coseries d = [ \f -> g [f x | x <- series d] + | g <- series d ] + +-- For customising the depth measure. Use with care! + +depth :: Int -> Int -> Int +depth d d' | d >= 0 = d'+1-d + | otherwise = error "SmallCheck.depth: argument < 0" + +dec :: Int -> Int +dec d | d > 0 = d-1 + | otherwise = error "SmallCheck.dec: argument <= 0" + +inc :: Int -> Int +inc d = d+1 + +-- show the extension of a function (in part, bounded both by +-- the number and depth of arguments) +instance (Serial a, Show a, Show b) => Show (a->b) where + show f = + if maxarheight == 1 + && sumarwidth + length ars * length "->;" < widthLimit then + "{"++( + concat $ intersperse ";" $ [a++"->"++r | (a,r) <- ars] + )++"}" + else + concat $ [a++"->\n"++indent r | (a,r) <- ars] + where + ars = take lengthLimit [ (show x, show (f x)) + | x <- series depthLimit ] + maxarheight = maximum [ max (height a) (height r) + | (a,r) <- ars ] + sumarwidth = sum [ length a + length r + | (a,r) <- ars] + indent = unlines . map (" "++) . lines + height = length . lines + (widthLimit,lengthLimit,depthLimit) = (80,20,3)::(Int,Int,Int) + +---------------- ------------------ + +-- adapted from QuickCheck originals: here results come in lists, +-- properties have depth arguments, stamps (for classifying random +-- tests) are omitted, existentials are introduced + +newtype PR = Prop [Result] + +data Result = Result {ok :: Maybe Bool, arguments :: [String]} + +nothing :: Result +nothing = Result {ok = Nothing, arguments = []} + +result :: Result -> PR +result res = Prop [res] + +newtype Property = Property (Int -> PR) + +class Testable a where + property :: a -> Int -> PR + +instance Testable Bool where + property b _ = Prop [Result (Just b) []] + +instance Testable PR where + property prop _ = prop + +instance (Serial a, Show a, Testable b) => Testable (a->b) where + property f = f' where Property f' = forAll series f + +instance Testable Property where + property (Property f) d = f d + +evaluate :: Testable a => a -> Series Result +evaluate x d = rs where Prop rs = property x d + +forAll :: (Show a, Testable b) => Series a -> (a->b) -> Property +forAll xs f = Property $ \d -> Prop $ + [ r{arguments = show x : arguments r} + | x <- xs d, r <- evaluate (f x) d ] + +forAllElem :: (Show a, Testable b) => [a] -> (a->b) -> Property +forAllElem xs = forAll (const xs) + +thereExists :: Testable b => Series a -> (a->b) -> Property +thereExists xs f = Property $ \d -> Prop $ + [ Result + ( Just $ or [ all pass (evaluate (f x) d) + | x <- xs d ] ) + [] ] + where + pass (Result Nothing _) = True + pass (Result (Just b) _) = b + +thereExistsElem :: Testable b => [a] -> (a->b) -> Property +thereExistsElem xs = thereExists (const xs) + +exists :: (Serial a, Testable b) => + (a->b) -> Property +exists = thereExists series + +existsDeeperBy :: (Serial a, Testable b) => + (Int->Int) -> (a->b) -> Property +existsDeeperBy f = thereExists (series . f) + +infixr 0 ==> + +(==>) :: Testable a => Bool -> a -> Property +True ==> x = Property (property x) +False ==> x = Property (const (result nothing)) + +--------------------- ---------------------- + +-- similar in spirit to QuickCheck but with iterative deepening + +-- test for values of depths 0..d stopping when a property +-- fails or when it has been checked for all these values +smallCheck :: Testable a => Int -> a -> IO String +smallCheck d = iterCheck 0 (Just d) + +depthCheck :: Testable a => Int -> a -> IO String +depthCheck d = iterCheck d (Just d) + +iterCheck :: Testable a => Int -> Maybe Int -> a -> IO String +iterCheck dFrom mdTo t = iter dFrom + where + iter :: Int -> IO String + iter d = do + let Prop results = property t d + (ok,s) <- check (mdTo==Nothing) 0 0 True results + maybe (iter (d+1)) + (\dTo -> if ok && d < dTo + then iter (d+1) + else return s) + mdTo + +check :: Bool -> Int -> Int -> Bool -> [Result] -> IO (Bool, String) +check i n x ok rs | null rs = do + let s = " Completed "++show n++" test(s)" + y = if i then "." else " without failure." + z | x > 0 = " But "++show x++" did not meet ==> condition." + | otherwise = "" + return (ok, s ++ y ++ z) + +check i n x ok (Result Nothing _ : rs) = do + progressReport i n x + check i (n+1) (x+1) ok rs + +check i n x f (Result (Just True) _ : rs) = do + progressReport i n x + check i (n+1) x f rs + +check i n x f (Result (Just False) args : rs) = do + let s = " Failed test no. "++show (n+1)++". Test values follow." + s' = s ++ ": " ++ concat (intersperse ", " args) + if i then + check i (n+1) x False rs + else + return (False, s') + +progressReport :: Bool -> Int -> Int -> IO () +progressReport _ _ _ = return () diff --git a/tests/examplefiles/Sudoku.lhs b/tests/examplefiles/Sudoku.lhs new file mode 100644 index 0000000..6829cf6 --- /dev/null +++ b/tests/examplefiles/Sudoku.lhs @@ -0,0 +1,382 @@ +% Copyright 2005 Brian Alliet + +\documentclass[11pt]{article} +\usepackage{palatino} +\usepackage{fullpage} +\usepackage{parskip} +\usepackage{lhs} + +\begin{document} + +\title{Sudoku Solver} +\author{Brian Alliet} +\maketitle + +\ignore{ +\begin{code} +module Sudoku ( + Sudoku, + makeSudoku, solve, eliminate, analyze, backtrack, + main + ) where + +import Array +import Monad +import List (union,intersperse,transpose,(\\),nub,nubBy) +\end{code} +} + +\section{Introduction} + +This Haskell module implements a solver for Sudoku~\footnote{http://en.wikipedia.org/wiki/Sudoku} puzzles. It can solve +any Sudoku puzzle, even those that require backtracking. + +\section{Data Types} + +\begin{code} +data CellState a = Known a | Unknown [a] | Impossible deriving Eq +\end{code} + +Each cell in a Sudoku grid can be in one of three states: ``Known'' if it has a known correct value~\footnote{Actually +this doesn't always means it is correct. While we are in the backtracking stage we make our guesses ``Known''.}, +``Unknown'' if there is still more than one possible correct value, or ``Impossible'' if there is no value that can +possibly fit the cell. Sudoku grids with ``Impossible'' cells are quickly discarded by the {\tt solve} function. + +\begin{code} +type Coords = (Int,Int) +type Grid a = Array Coords (CellState a) +newtype Sudoku a = Sudoku { unSudoku :: Grid a } deriving Eq +\end{code} + +We represent a Sudoku grid as an Array indexed by integer coordinates. We additionally define a newtype wrapper for the +grid. The smart constructor, {\tt makeSudoku} verifies some invariants before creating the Sudoku value. All the public +API functions operate on the Sudoku type. + +\begin{code} +instance Show a => Show (Sudoku a) where showsPrec p = showParen (p>0) . showsGrid . unSudoku +instance Show a => Show (CellState a) where showsPrec _ = showsCell +\end{code} + +We define {\tt Show} instances for the above types. + +\section{Internal Functions} + +\begin{code} +size :: Grid a -> Int +size = (+1).fst.snd.bounds +\end{code} + +{\tt size} returns the size (the width, height, and number of subboxes) for a Sudoku grid. We ensure Grid's are always +square and indexed starting at $(0,0)$ so simply incrementing either of the array's upper bounds is correct. + +\begin{code} +getRow,getCol,getBox :: Grid a -> Int -> [(Coords,CellState a)] +getRow grid r = [let l = (r,c) in (l,grid!l)|c <- [0..size grid - 1]] +getCol grid c = [let l = (r,c) in (l,grid!l)|r <- [0..size grid - 1]] +getBox grid b = [let l = (r,c) in (l,grid!l)|r <- [boxR..boxR+boxN-1],c <- [boxC..boxC+boxN-1]] + where + boxN = intSqrt (size grid); boxR = b `quot` boxN * boxN; boxC = b `rem` boxN * boxN + +getBoxOf :: Grid a -> Coords -> [(Coords,CellState a)] +getBoxOf grid (r,c) = grid `getBox` ((r `quot` boxN * boxN) + (c `quot` boxN)) + where boxN = intSqrt (size grid) +\end{code} + +{\tt getRow}, {\tt getCol}, and {\tt getBox} return the coordinates and values of the cell in row, column, or box +number {\tt n}, {\tt r}, or {\tt b}. + +\begin{code} +getNeighbors :: Eq a => Grid a -> Coords -> [(Coords,CellState a)] +getNeighbors grid l@(r,c) = filter ((/=l).fst) + $ foldr (union.($grid)) [] + [(`getRow`r),(`getCol`c),(`getBoxOf`l)] +\end{code} + +{\tt getNeighbors} returns the coordinates and values of all the neighbors of this cell. + +\begin{code} +impossible :: Eq a => Grid a -> Coords -> [a] +impossible grid l = map snd $ justKnowns $ grid `getNeighbors` l +\end{code} + +{\tt impossible} returns a list of impossible values for a given cell. The impossible values consist of the values any +``Known'' neighbors. + +\begin{code} +justUnknowns :: [(Coords,CellState a)] -> [(Coords,[a])] +justUnknowns = foldr (\c -> case c of (p,Unknown xs) -> ((p,xs):); _ -> id) [] + +justKnowns :: [(Coords,CellState a)] -> [(Coords,a)] +justKnowns = foldr (\c -> case c of (p,Known x) -> ((p,x):); _ -> id) [] +\end{code} + +{\tt justUnknowns} and {\tt justKnowns} return only the Known or Unknown values (with the constructor stripped off) +from a list of cells. + +\begin{code} +updateGrid :: Grid a -> [(Coords,CellState a)] -> Maybe (Grid a) +updateGrid _ [] = Nothing +updateGrid grid xs = Just $ grid // nubBy (\(x,_) (y,_) -> x==y) xs +\end{code} + +{\tt updateGrid} applies a set of updates to a grid and returns the new grid only if it was updated. + +\section{Public API} + +\begin{code} +makeSudoku :: (Num a, Ord a, Enum a) => [[a]] -> Sudoku a +makeSudoku xs + | not (all ((==size).length) xs) = error "error not a square" + | (intSqrt size)^(2::Int) /= size = error "error dims aren't perfect squares" + | any (\x -> x < 0 || x > fromIntegral size) (concat xs) = error "value out of range" + | otherwise = Sudoku (listArray ((0,0),(size-1,size-1)) states) + where + size = length xs + states = map f (concat xs) + f 0 = Unknown [1..fromIntegral size] + f x = Known x +\end{code} + +{\tt makeSudoku} makes a {\tt Sudoku} value from a list of numbers. The given matrix must be square and have dimensions +that are a perfect square. The possible values for each cell range from 1 to the dimension of the square with ``0'' +representing unknown values.\footnote{The rest of the code doesn't depend on any of this weird ``0'' is unknown +representation. In fact, it doesn't depend on numeric values at all. ``0'' is just used here because it makes +representing grids in Haskell source code easier.} + +\begin{code} +eliminate :: Eq a => Sudoku a -> Maybe (Sudoku a) +eliminate (Sudoku grid) = fmap Sudoku $ updateGrid grid changes >>= sanitize + where + changes = concatMap findChange $ assocs grid + findChange (l,Unknown xs) + = map ((,) l) + $ case filter (not.(`elem`impossible grid l)) xs of + [] -> return Impossible + [x] -> return $ Known x + xs' + | xs' /= xs -> return $ Unknown xs' + | otherwise -> mzero + findChange _ = mzero + sanitize grid = return $ grid // [(l,Impossible) | + (l,x) <- justKnowns changes, x `elem` impossible grid l] +\end{code} + +The {\tt eliminate} phase tries to remove possible choices for ``Unknowns'' based on ``Known'' values in the same row, +column, or box as the ``Unknown'' value. For each cell on the grid we find its ``neighbors'', that is, cells in the +same row, column, or box. Out of those neighbors we get a list of all the ``Known'' values. We can eliminate all of +these from our list of candidates for this cell. If we're lucky enough to eliminate all the candidates but one we have +a new ``Known'' value. If we're unlucky enough to have eliminates {\bf all} the possible candidates we have a new +``Impossible'' value. + +After iterating though every cell we make one more pass looking for conflicting changes. {\tt sanitize} marks cells as +``Impossible'' if we have conflicting ``Known'' values. + +\begin{code} +analyze :: Eq a => Sudoku a -> Maybe (Sudoku a) +analyze (Sudoku grid) = fmap Sudoku $ updateGrid grid $ nub [u | + f <- map ($grid) [getRow,getCol,getBox], + n <- [0..size grid - 1], + u <- unique (f n)] + where + unique xs = foldr f [] $ foldr (union.snd) [] unknowns \\ map snd (justKnowns xs) + where + unknowns = justUnknowns xs + f c = case filter ((c`elem`).snd) unknowns of + [(p,_)] -> ((p,Known c):) + _ -> id +\end{code} + +The {\tt analyze} phase tries to turn ``Unknowns'' into ``Knowns'' when a certain ``Unknown'' is the only cell that +contains a value needed in a given row, column, or box. We apply each of the functions {\tt getRow}, {\tt getCol}, and +{\tt getBox} to all the indices on the grid, apply {\tt unique} to each group, and update the array with the +results. {\tt unique} gets a list of all the unknown cells in the group and finds all the unknown values in each of +those cells. Each of these values are iterated though looking for a value that is only contained in one cell. If such a +value is found the cell containing it must be that value. + +\begin{code} +backtrack :: (MonadPlus m, Eq a) => Sudoku a -> m (Sudoku a) +backtrack (Sudoku grid) = case (justUnknowns (assocs grid)) of + [] -> return $ Sudoku grid + ((p,xs):_) -> msum $ map (\x -> solve $ Sudoku $ grid // [(p,Known x)]) xs +\end{code} + +Sometimes the above two phases still aren't enough to solve a puzzle. For these rare puzzles backtracking is required. +We attempt to solve the puzzle by replacing the first ``Unknown'' value with each of the candidate values and solving +the resulting puzzles. Hopefully at least one of our choices will result in a solvable puzzle. + +We could actually solve any puzzle using backtracking alone, although this would be very inefficient. The above +functions simplify most puzzles enough that the backtracking phase has to do hardly any work. + +\begin{code} +solve :: (MonadPlus m, Eq a) => Sudoku a -> m (Sudoku a) +solve sudoku = + case eliminate sudoku of + Just new + | any (==Impossible) (elems (unSudoku new))-> mzero + | otherwise -> solve new + Nothing -> case analyze sudoku of + Just new -> solve new + Nothing -> backtrack sudoku +\end{code} + +{\tt solve} glues all the above phases together. First we run the {\tt eliminate} phase. If that found the puzzle to +be unsolvable we abort immediately. If {\tt eliminate} changed the grid we go though the {\tt eliminate} phase again +hoping to eliminate more. Once {\tt eliminate} can do no more work we move on to the {\tt analyze} phase. If this +succeeds in doing some work we start over again with the {\tt eliminate} phase. Once {\tt analyze} can do no more work +we have no choice but to resort to backtracking. (However in most cases backtracking won't actually do anything because +the puzzle is already solved.) + +\begin{code} +showsCell :: Show a => CellState a -> ShowS +showsCell (Known x) = shows x +showsCell (Impossible) = showChar 'X' +showsCell (Unknown xs) = \rest -> ('(':) + $ foldr id (')':rest) + $ intersperse (showChar ' ') + $ map shows xs +\end{code} + +{\tt showCell} shows a cell. + +\begin{code} +showsGrid :: Show a => Grid a -> ShowS +showsGrid grid = showsTable [[grid!(r,c) | c <- [0..size grid-1]] | r <- [0..size grid-1]] +\end{code} + +{\tt showGrid} show a grid. + +\begin{code} +-- FEATURE: This is pretty inefficient +showsTable :: Show a => [[a]] -> ShowS +showsTable xs = (showChar '\n' .) $ showString $ unlines $ map (concat . intersperse " ") xs'' + where + xs' = (map.map) show xs + colWidths = map (max 2 . maximum . map length) (transpose xs') + xs'' = map (zipWith (\n s -> s ++ (replicate (n - length s) ' ')) colWidths) xs' +\end{code} + +{\tt showsTable} shows a table (or matrix). Every column has the same width so things line up. + +\begin{code} +intSqrt :: Integral a => a -> a +intSqrt n + | n < 0 = error "intSqrt: negative n" + | otherwise = f n + where + f x = if y < x then f y else x + where y = (x + (n `quot` x)) `quot` 2 +\end{code} + +{\tt intSqrt} is Newton`s Iteration for finding integral square roots. + +\ignore{ +\begin{code} +test :: Sudoku Int +test = makeSudoku [ + [0,6,0,1,0,4,0,5,0], + [0,0,8,3,0,5,6,0,0], + [2,0,0,0,0,0,0,0,1], + [8,0,0,4,0,7,0,0,6], + [0,0,6,0,0,0,3,0,0], + [7,0,0,9,0,1,0,0,4], + [5,0,0,0,0,0,0,0,2], + [0,0,7,2,0,6,9,0,0], + [0,4,0,5,0,8,0,7,0]] + +test2 :: Sudoku Int +test2 = makeSudoku [ + [0,7,0,0,0,0,8,0,0], + [0,0,0,2,0,4,0,0,0], + [0,0,6,0,0,0,0,3,0], + [0,0,0,5,0,0,0,0,6], + [9,0,8,0,0,2,0,4,0], + [0,5,0,0,3,0,9,0,0], + [0,0,2,0,8,0,0,6,0], + [0,6,0,9,0,0,7,0,1], + [4,0,0,0,0,3,0,0,0]] + +testSmall :: Sudoku Int +testSmall = makeSudoku [ + [1,0,0,0,0,0,0,0,0], + [0,0,2,7,4,0,0,0,0], + [0,0,0,5,0,0,0,0,4], + [0,3,0,0,0,0,0,0,0], + [7,5,0,0,0,0,0,0,0], + [0,0,0,0,0,9,6,0,0], + [0,4,0,0,0,6,0,0,0], + [0,0,0,0,0,0,0,7,1], + [0,0,0,0,0,1,0,3,0]] + +testHard :: Sudoku Int +testHard = makeSudoku [ + [0,0,0,8,0,2,0,0,0], + [5,0,0,0,0,0,0,0,1], + [0,0,6,0,5,0,3,0,0], + [0,0,9,0,1,0,8,0,0], + [1,0,0,0,0,0,0,0,2], + [0,0,0,9,0,7,0,0,0], + [0,6,1,0,3,0,7,8,0], + [0,5,0,0,0,0,0,4,0], + [0,7,2,0,4,0,1,5,0]] + +testHard2 :: Sudoku Int +testHard2 = makeSudoku [ + [3,0,0,2,0,0,9,0,0], + [0,0,0,0,0,0,0,0,5], + [0,7,0,1,0,4,0,0,0], + [0,0,9,0,0,0,8,0,0], + [5,0,0,0,7,0,0,0,6], + [0,0,1,0,0,0,2,0,0], + [0,0,0,3,0,9,0,4,0], + [8,0,0,0,0,0,0,0,0], + [0,0,6,0,0,5,0,0,7]] + +testHW :: Sudoku Int +testHW = makeSudoku [ + [0,0,0,1,0,0,7,0,2], + [0,3,0,9,5,0,0,0,0], + [0,0,1,0,0,2,0,0,3], + [5,9,0,0,0,0,3,0,1], + [0,2,0,0,0,0,0,7,0], + [7,0,3,0,0,0,0,9,8], + [8,0,0,2,0,0,1,0,0], + [0,0,0,0,8,5,0,6,0], + [6,0,5,0,0,9,0,0,0]] + +testTough :: Sudoku Int +testTough = makeSudoku $ map (map read . words) $ lines $ + "8 3 0 0 0 0 0 4 6\n"++ + "0 2 0 1 0 4 0 3 0\n"++ + "0 0 0 0 0 0 0 0 0\n"++ + "0 0 2 9 0 6 5 0 0\n"++ + "1 4 0 0 0 0 0 2 3\n"++ + "0 0 5 4 0 3 1 0 0\n"++ + "0 0 0 0 0 0 0 0 0\n"++ + "0 6 0 3 0 8 0 7 0\n"++ + "9 5 0 0 0 0 0 6 2\n" + +testDiabolical :: Sudoku Int +testDiabolical = makeSudoku $ map (map read . words) $ lines $ + "8 0 0 7 0 1 0 0 2\n"++ + "0 0 6 0 0 0 7 0 0\n"++ + "0 1 7 0 0 0 8 9 0\n"++ + "0 0 0 1 7 3 0 0 0\n"++ + "7 0 0 0 0 0 0 0 6\n"++ + "0 0 0 9 5 6 0 0 0\n"++ + "0 9 5 0 0 0 4 1 0\n"++ + "0 0 8 0 0 0 5 0 0\n"++ + "3 0 0 6 0 5 0 0 7\n" + +main :: IO () +main = do + let + solve' p = case solve p of + [] -> fail $ "couldn't solve: " ++ show p + sols -> return sols + mapM_ (\p -> solve' p >>= putStrLn.show) [test,test2,testSmall,testHard,testHard2,testHW,testTough,testDiabolical] + return () + +\end{code} +} + +\end{document} diff --git a/tests/examplefiles/apache2.conf b/tests/examplefiles/apache2.conf new file mode 100644 index 0000000..d0e838e --- /dev/null +++ b/tests/examplefiles/apache2.conf @@ -0,0 +1,393 @@ +# Based upon the NCSA server configuration files originally by Rob McCool. +# Changed extensively for the Debian package by Daniel Stone +# and also by Thom May . + +# ServerRoot: The top of the directory tree under which the server's +# configuration, error, and log files are kept. +# +# NOTE! If you intend to place this on an NFS (or otherwise network) +# mounted filesystem then please read the LockFile documentation +# (available at ); +# you will save yourself a lot of trouble. + +ServerRoot "/etc/apache2" + +# The LockFile directive sets the path to the lockfile used when Apache +# is compiled with either USE_FCNTL_SERIALIZED_ACCEPT or +# USE_FLOCK_SERIALIZED_ACCEPT. This directive should normally be left at +# its default value. The main reason for changing it is if the logs +# directory is NFS mounted, since the lockfile MUST BE STORED ON A LOCAL +# DISK. The PID of the main server process is automatically appended to +# the filename. + +LockFile /var/lock/apache2/accept.lock + +# PidFile: The file in which the server should record its process +# identification number when it starts. + +PidFile /var/run/apache2.pid + +# Timeout: The number of seconds before receives and sends time out. + +Timeout 300 + +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. + +KeepAlive On + +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We recommend you leave this number high, for maximum performance. + +MaxKeepAliveRequests 100 + +# KeepAliveTimeout: Number of seconds to wait for the next request from the +# same client on the same connection. + +KeepAliveTimeout 15 + +## +## Server-Pool Size Regulation (MPM specific) +## + +# prefork MPM +# StartServers ......... number of server processes to start +# MinSpareServers ...... minimum number of server processes which are kept spare +# MaxSpareServers ...... maximum number of server processes which are kept spare +# MaxClients ........... maximum number of server processes allowed to start +# MaxRequestsPerChild .. maximum number of requests a server process serves + +StartServers 5 +MinSpareServers 5 +MaxSpareServers 10 +MaxClients 20 +MaxRequestsPerChild 0 + + +# pthread MPM +# StartServers ......... initial number of server processes to start +# MaxClients ........... maximum number of server processes allowed to start +# MinSpareThreads ...... minimum number of worker threads which are kept spare +# MaxSpareThreads ...... maximum number of worker threads which are kept spare +# ThreadsPerChild ...... constant number of worker threads in each server process +# MaxRequestsPerChild .. maximum number of requests a server process serves + +StartServers 2 +MaxClients 150 +MinSpareThreads 25 +MaxSpareThreads 75 +ThreadsPerChild 25 +MaxRequestsPerChild 0 + + +# perchild MPM +# NumServers ........... constant number of server processes +# StartThreads ......... initial number of worker threads in each server process +# MinSpareThreads ...... minimum number of worker threads which are kept spare +# MaxSpareThreads ...... maximum number of worker threads which are kept spare +# MaxThreadsPerChild ... maximum number of worker threads in each server process +# MaxRequestsPerChild .. maximum number of connections per server process (then it dies) + +NumServers 5 +StartThreads 5 +MinSpareThreads 5 +MaxSpareThreads 10 +MaxThreadsPerChild 20 +MaxRequestsPerChild 0 +AcceptMutex fcntl + + +User www-data +Group www-data + +# The following directives define some format nicknames for use with +# a CustomLog directive (see below). +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent + + +# Global error log. +ErrorLog /var/log/apache2/error.log + +# Include module configuration: +Include /etc/apache2/mods-enabled/*.load +Include /etc/apache2/mods-enabled/*.conf + +# Include all the user configurations: +Include /etc/apache2/httpd.conf + +# Include ports listing +Include /etc/apache2/ports.conf + +# Include generic snippets of statements +Include /etc/apache2/conf.d/[^.#]* + +#Let's have some Icons, shall we? +Alias /icons/ "/usr/share/apache2/icons/" + + Options Indexes MultiViews + AllowOverride None + Order allow,deny + Allow from all + + +# Set up the default error docs. +# +# Customizable error responses come in three flavors: +# 1) plain text 2) local redirects 3) external redirects +# +# Some examples: +#ErrorDocument 500 "The server made a boo boo." +#ErrorDocument 404 /missing.html +#ErrorDocument 404 "/cgi-bin/missing_handler.pl" +#ErrorDocument 402 http://www.example.com/subscription_info.html +# + +# +# Putting this all together, we can Internationalize error responses. +# +# We use Alias to redirect any /error/HTTP_.html.var response to +# our collection of by-error message multi-language collections. We use +# includes to substitute the appropriate text. +# +# You can modify the messages' appearance without changing any of the +# default HTTP_.html.var files by adding the line; +# +# Alias /error/include/ "/your/include/path/" +# +# which allows you to create your own set of files by starting with the +# /usr/local/apache2/error/include/ files and +# copying them to /your/include/path/, even on a per-VirtualHost basis. +# + + + + Alias /error/ "/usr/share/apache2/error/" + + + AllowOverride None + Options IncludesNoExec + AddOutputFilter Includes html + AddHandler type-map var + Order allow,deny + Allow from all + LanguagePriority en es de fr + ForceLanguagePriority Prefer Fallback + + + ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var + ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var + ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var + ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var + ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var + ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var + ErrorDocument 410 /error/HTTP_GONE.html.var + ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var + ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var + ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var + ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var + ErrorDocument 415 /error/HTTP_SERVICE_UNAVAILABLE.html.var + ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var + ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var + ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var + ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var + ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var + + + + +DirectoryIndex index.html index.cgi index.pl index.php index.xhtml + +# UserDir is now a module +#UserDir public_html +#UserDir disabled root + +# +# AllowOverride FileInfo AuthConfig Limit +# Options Indexes SymLinksIfOwnerMatch IncludesNoExec +# + +AccessFileName .htaccess + + + Order allow,deny + Deny from all + + +UseCanonicalName Off + +TypesConfig /etc/mime.types +DefaultType text/plain + +HostnameLookups Off + +IndexOptions FancyIndexing VersionSort + +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +# This really should be .jpg. + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif core + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + + +# This is from Matty J's patch. Anyone want to make the icons? +#AddIcon /icons/dirsymlink.jpg ^^SYMDIR^^ +#AddIcon /icons/symlink.jpg ^^SYMLINK^^ + +DefaultIcon /icons/unknown.gif + +ReadmeName README.html +HeaderName HEADER.html + +IndexIgnore .??* *~ *# HEADER* RCS CVS *,t + +AddEncoding x-compress Z +AddEncoding x-gzip gz tgz + +AddLanguage da .dk +AddLanguage nl .nl +AddLanguage en .en +AddLanguage et .et +AddLanguage fr .fr +AddLanguage de .de +AddLanguage el .el +AddLanguage it .it +AddLanguage ja .ja +AddLanguage pl .po +AddLanguage ko .ko +AddLanguage pt .pt +AddLanguage no .no +AddLanguage pt-br .pt-br +AddLanguage ltz .ltz +AddLanguage ca .ca +AddLanguage es .es +AddLanguage sv .se +AddLanguage cz .cz +AddLanguage ru .ru +AddLanguage tw .tw +AddLanguage zh-tw .tw + +LanguagePriority en da nl et fr de el it ja ko no pl pt pt-br ltz ca es sv tw + + +#AddDefaultCharset ISO-8859-1 + +AddCharset ISO-8859-1 .iso8859-1 .latin1 +AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen +AddCharset ISO-8859-3 .iso8859-3 .latin3 +AddCharset ISO-8859-4 .iso8859-4 .latin4 +AddCharset ISO-8859-5 .iso8859-5 .latin5 .cyr .iso-ru +AddCharset ISO-8859-6 .iso8859-6 .latin6 .arb +AddCharset ISO-8859-7 .iso8859-7 .latin7 .grk +AddCharset ISO-8859-8 .iso8859-8 .latin8 .heb +AddCharset ISO-8859-9 .iso8859-9 .latin9 .trk +AddCharset ISO-2022-JP .iso2022-jp .jis +AddCharset ISO-2022-KR .iso2022-kr .kis +AddCharset ISO-2022-CN .iso2022-cn .cis +AddCharset Big5 .Big5 .big5 +# For russian, more than one charset is used (depends on client, mostly): +AddCharset WINDOWS-1251 .cp-1251 .win-1251 +AddCharset CP866 .cp866 +AddCharset KOI8-r .koi8-r .koi8-ru +AddCharset KOI8-ru .koi8-uk .ua +AddCharset ISO-10646-UCS-2 .ucs2 +AddCharset ISO-10646-UCS-4 .ucs4 +AddCharset UTF-8 .utf8 + +AddCharset GB2312 .gb2312 .gb +AddCharset utf-7 .utf7 +AddCharset utf-8 .utf8 +AddCharset big5 .big5 .b5 +AddCharset EUC-TW .euc-tw +AddCharset EUC-JP .euc-jp +AddCharset EUC-KR .euc-kr +AddCharset shift_jis .sjis + +#AddType application/x-httpd-php .php +#AddType application/x-httpd-php-source .phps + +AddType application/x-tar .tgz + +# To use CGI scripts outside /cgi-bin/: +# +#AddHandler cgi-script .cgi + +# To use server-parsed HTML files +# + + SetOutputFilter INCLUDES + + +# If you wish to use server-parsed imagemap files, use +# +#AddHandler imap-file map + +BrowserMatch "Mozilla/2" nokeepalive +BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 +BrowserMatch "RealPlayer 4\.0" force-response-1.0 +BrowserMatch "Java/1\.0" force-response-1.0 +BrowserMatch "JDK/1\.0" force-response-1.0 + +# +# The following directive disables redirects on non-GET requests for +# a directory that does not include the trailing slash. This fixes a +# problem with Microsoft WebFolders which does not appropriately handle +# redirects for folders with DAV methods. +# + +BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully +BrowserMatch "^WebDrive" redirect-carefully +BrowserMatch "^gnome-vfs" redirect-carefully +BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully + +# Allow server status reports, with the URL of http://servername/server-status +# Change the ".your_domain.com" to match your domain to enable. +# +# +# SetHandler server-status +# Order deny,allow +# Deny from all +# Allow from .your_domain.com +# + +# Allow remote server configuration reports, with the URL of +# http://servername/server-info (requires that mod_info.c be loaded). +# Change the ".your_domain.com" to match your domain to enable. +# +# +# SetHandler server-info +# Order deny,allow +# Deny from all +# Allow from .your_domain.com +# + +# Include the virtual host configurations: +Include /etc/apache2/sites-enabled/[^.#]* diff --git a/tests/examplefiles/as3_test.as b/tests/examplefiles/as3_test.as new file mode 100644 index 0000000..7e19f88 --- /dev/null +++ b/tests/examplefiles/as3_test.as @@ -0,0 +1,143 @@ + import flash.events.MouseEvent; + import com.example.programmingas3.playlist.PlayList; + import com.example.programmingas3.playlist.Song; + import com.example.programmingas3.playlist.SortProperty; + + // constants for the different "states" of the song form + private static const ADD_SONG:uint = 1; + private static const SONG_DETAIL:uint = 2; + + private var playList:PlayList = new PlayList(); + + private function initApp():void + { + // set the initial state of the song form, for adding a new song + setFormState(ADD_SONG); + + // prepopulate the list with a few songs + playList.addSong(new Song("Nessun Dorma", "Luciano Pavarotti", 1990, "nessundorma.mp3", ["90's", "Opera"])); + playList.addSong(new Song("Come Undone", "Duran Duran", 1993, "comeundone.mp3", ["90's", "Pop"])); + playList.addSong(new Song("Think of Me", "Sarah Brightman", 1987, "thinkofme.mp3", ["Showtunes"])); + playList.addSong(new Song("Unbelievable", "EMF", 1991, "unbelievable.mp3", ["90's", "Pop"])); + + songList.dataProvider = playList.songList; + } + + + private function sortList(sortField:SortProperty):void + { + // Make all the sort type buttons enabled. + // The active one will be grayed-out below + sortByTitle.selected = false; + sortByArtist.selected = false; + sortByYear.selected = false; + + switch (sortField) + { + case SortProperty.TITLE: + sortByTitle.selected = true; + break; + case SortProperty.ARTIST: + sortByArtist.selected = true; + break; + case SortProperty.YEAR: + sortByYear.selected = true; + break; + } + + playList.sortList(sortField); + + refreshList(); + } + + + private function refreshList():void + { + // remember which song was selected + var selectedSong:Song = Song(songList.selectedItem); + + // re-assign the song list as the dataprovider to get the newly sorted list + // and force the List control to refresh itself + songList.dataProvider = playList.songList; + + // reset the song selection + if (selectedSong != null) + { + songList.selectedItem = selectedSong; + } + } + + + private function songSelectionChange():void + { + if (songList.selectedIndex != -1) + { + setFormState(SONG_DETAIL); + } + else + { + setFormState(ADD_SONG); + } + } + + + private function addNewSong():void + { + // gather the values from the form and add the new song + var title:String = newSongTitle.text; + var artist:String = newSongArtist.text; + var year:uint = newSongYear.value; + var filename:String = newSongFilename.text; + var genres:Array = newSongGenres.selectedItems; + + playList.addSong(new Song(title, artist, year, filename, genres)); + + refreshList(); + + // clear out the "add song" form fields + setFormState(ADD_SONG); + } + + + private function songListLabel(item:Object):String + { + return item.toString(); + } + + + private function setFormState(state:uint):void + { + // set the form title and control state + switch (state) + { + case ADD_SONG: + formTitle.text = "Add New Song"; + // show the submit button + submitSongData.visible = true; + showAddControlsBtn.visible = false; + // clear the form fields + newSongTitle.text = ""; + newSongArtist.text = ""; + newSongYear.value = (new Date()).fullYear; + newSongFilename.text = ""; + newSongGenres.selectedIndex = -1; + // deselect the currently selected song (if any) + songList.selectedIndex = -1; + break; + + case SONG_DETAIL: + formTitle.text = "Song Details"; + // populate the form with the selected item's data + var selectedSong:Song = Song(songList.selectedItem); + newSongTitle.text = selectedSong.title; + newSongArtist.text = selectedSong.artist; + newSongYear.value = selectedSong.year; + newSongFilename.text = selectedSong.filename; + newSongGenres.selectedItems = selectedSong.genres; + // hide the submit button + submitSongData.visible = false; + showAddControlsBtn.visible = true; + break; + } + } + diff --git a/tests/examplefiles/as3_test2.as b/tests/examplefiles/as3_test2.as new file mode 100644 index 0000000..630ea72 --- /dev/null +++ b/tests/examplefiles/as3_test2.as @@ -0,0 +1,46 @@ +package ru.dfls.events { + import flash.events.Event; + import flash.events.ErrorEvent; + + /** + * This event is usually dispatched if some error was thrown from an asynchronous code, i.e. there + * is no relevant user stack part to process the error. There is only one type of such event: + * ErrorEvent.ERROR which is same as flash.events.ErrorEvent.ERROR. + * The only difference between flash.events.ErrorEvent and + * ru.dfls.events.ErrorEvent is the capability of the latter to store the underlying cause + * (the Error). + * + * @see flash.events.ErrorEvent + * @see Error + * @author dragonfly + */ + public class ErrorEvent extends flash.events.ErrorEvent { + + public static var ERROR : String = flash.events.ErrorEvent.ERROR; + + private var _error : Error; + + public function ErrorEvent(type : String, bubbles : Boolean = false, cancelable : Boolean = false, + text : String = "", error : Error = null) { + super(type, bubbles, cancelable, text); + _error = error; + } + + public function get error() : Error { + return _error; + } + + public function set error(value : Error) : void { + _error = value; + } + + public override function toString() : String { + return formatToString("ErrorEvent", "type", "bubbles", "cancelable", "eventPhase", "text", "error"); + } + + public override function clone() : Event { + return new ru.dfls.events.ErrorEvent(type, bubbles, cancelable, text, error); + } + + } +} diff --git a/tests/examplefiles/as3_test3.as b/tests/examplefiles/as3_test3.as new file mode 100644 index 0000000..b695486 --- /dev/null +++ b/tests/examplefiles/as3_test3.as @@ -0,0 +1,3 @@ +protected function remote(method : String, ...args : Array) : Boolean { + return true; +} diff --git a/tests/examplefiles/badcase.java b/tests/examplefiles/badcase.java new file mode 100644 index 0000000..dc9b2e7 --- /dev/null +++ b/tests/examplefiles/badcase.java @@ -0,0 +1,2 @@ +// this used to take ages +void foo() throws xxxxxxxxxxxxxxxxxxxxxx{ } diff --git a/tests/examplefiles/batchfile.bat b/tests/examplefiles/batchfile.bat new file mode 100644 index 0000000..5cdc625 --- /dev/null +++ b/tests/examplefiles/batchfile.bat @@ -0,0 +1,49 @@ +rem this is a demo file. +@rem +@echo off + +call c:\temp.bat somearg +call :lab somearg +rem This next one is wrong in the vim lexer! +call c:temp.bat + +echo "Hi!" +echo hi +echo on +echo off +echo. +@echo off +if exist *.log echo The log file has arrived. +rem These are all escapes, also done incorrectly by the vim lexer +echo ^^ ^> ^< ^| + +x=beginning +setlocal +x = new text +endlocal + +echo testrem x +echo test rem x + +for %%var in (*.jpg) do echo %%var +for /D %%var in (a b c) do echo %%var +for /R C:\temp %%var in (*.jpg) do iexplore.exe %%var +rem Vim has this one wrong too. +for /L %%var in (10,-1,1) do echo %%var +for /F %%var in ("hi!") do echo %%var +for /F "eol=c,skip=1,usebackq" %%var in (`command`) do echo %%var %~l %~fl %~dl %~pl %~nl %~xl %~sl %~al %~tl %~zl %~$PATH:l %~dpl %~dp$PATH:l %~ftzal + +echo some file ?! > somefile.txt + +set PATH=%PATH%;c:\windows + +goto answer%errorlevel% + :answer0 + echo Hi it's zero + :answer1 + echo New + +if exist a del a +else echo A is missing! + + diff --git a/tests/examplefiles/boot-9.scm b/tests/examplefiles/boot-9.scm new file mode 100644 index 0000000..1f6ae24 --- /dev/null +++ b/tests/examplefiles/boot-9.scm @@ -0,0 +1,1557 @@ +;;; installed-scm-file + +;;;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. +;;;; +;;;; 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, or (at your option) +;;;; any later version. +;;;; +;;;; 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. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this software; see the file COPYING. If not, write to +;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +;;;; Boston, MA 02111-1307 USA +;;;; +;;;; As a special exception, the Free Software Foundation gives permission +;;;; for additional uses of the text contained in its release of GUILE. +;;;; +;;;; The exception is that, if you link the GUILE library with other files +;;;; to produce an executable, this does not by itself cause the +;;;; resulting executable to be covered by the GNU General Public License. +;;;; Your use of that executable is in no way restricted on account of +;;;; linking the GUILE library code into it. +;;;; +;;;; This exception does not however invalidate any other reasons why +;;;; the executable file might be covered by the GNU General Public License. +;;;; +;;;; This exception applies only to the code released by the +;;;; Free Software Foundation under the name GUILE. If you copy +;;;; code from other Free Software Foundation releases into a copy of +;;;; GUILE, as the General Public License permits, the exception does +;;;; not apply to the code that you add in this way. To avoid misleading +;;;; anyone as to the status of such modified files, you must delete +;;;; this exception notice from them. +;;;; +;;;; If you write modifications of your own for GUILE, it is your choice +;;;; whether to permit this exception to apply to your modifications. +;;;; If you do not wish that, delete this exception notice. +;;;; + + +;;; Commentary: + +;;; This file is the first thing loaded into Guile. It adds many mundane +;;; definitions and a few that are interesting. +;;; +;;; The module system (hence the hierarchical namespace) are defined in this +;;; file. +;;; + +;;; Code: + + +;;; {Deprecation} +;;; + +;; We don't have macros here, but we do want to define +;; `begin-deprecated' early. + +(define begin-deprecated + (procedure->memoizing-macro + (lambda (exp env) + (if (include-deprecated-features) + `(begin ,@(cdr exp)) + `#f)))) + + +;;; {Features} +;; + +(define (provide sym) + (if (not (memq sym *features*)) + (set! *features* (cons sym *features*)))) + +;;; Return #t iff FEATURE is available to this Guile interpreter. +;;; In SLIB, provided? also checks to see if the module is available. +;;; We should do that too, but don't. +(define (provided? feature) + (and (memq feature *features*) #t)) + +(begin-deprecated + (define (feature? sym) + (issue-deprecation-warning + "`feature?' is deprecated. Use `provided?' instead.") + (provided? sym))) + +;;; let format alias simple-format until the more complete version is loaded +(define format simple-format) + + +;;; {R4RS compliance} + +(primitive-load-path "ice-9/r4rs.scm") + + +;;; {Simple Debugging Tools} +;; + + +;; peek takes any number of arguments, writes them to the +;; current ouput port, and returns the last argument. +;; It is handy to wrap around an expression to look at +;; a value each time is evaluated, e.g.: +;; +;; (+ 10 (troublesome-fn)) +;; => (+ 10 (pk 'troublesome-fn-returned (troublesome-fn))) +;; + +(define (peek . stuff) + (newline) + (display ";;; ") + (write stuff) + (newline) + (car (last-pair stuff))) + +(define pk peek) + +(define (warn . stuff) + (with-output-to-port (current-error-port) + (lambda () + (newline) + (display ";;; WARNING ") + (display stuff) + (newline) + (car (last-pair stuff))))) + + +;;; {Trivial Functions} +;;; + +(define (identity x) x) +(define (1+ n) (+ n 1)) +(define (1- n) (+ n -1)) +(define (and=> value procedure) (and value (procedure value))) +(define (make-hash-table k) (make-vector k '())) + +(begin-deprecated + (define (id x) + (issue-deprecation-warning "`id' is deprecated. Use `identity' instead.") + (identity x)) + (define (-1+ n) + (issue-deprecation-warning "`-1+' is deprecated. Use `1-' instead.") + (1- n)) + (define (return-it . args) + (issue-deprecation-warning "`return-it' is deprecated. Use `noop' instead.") + (apply noop args))) + +;;; apply-to-args is functionally redundant with apply and, worse, +;;; is less general than apply since it only takes two arguments. +;;; +;;; On the other hand, apply-to-args is a syntacticly convenient way to +;;; perform binding in many circumstances when the "let" family of +;;; of forms don't cut it. E.g.: +;;; +;;; (apply-to-args (return-3d-mouse-coords) +;;; (lambda (x y z) +;;; ...)) +;;; + +(define (apply-to-args args fn) (apply fn args)) + + + +;;; {Integer Math} +;;; + +(define (ipow-by-squaring x k acc proc) + (cond ((zero? k) acc) + ((= 1 k) (proc acc x)) + (else (ipow-by-squaring (proc x x) + (quotient k 2) + (if (even? k) acc (proc acc x)) + proc)))) + +(begin-deprecated + (define (string-character-length s) + (issue-deprecation-warning "`string-character-length' is deprecated. Use `string-length' instead.") + (string-length s)) + (define (flags . args) + (issue-deprecation-warning "`flags' is deprecated. Use `logior' instead.") + (apply logior args))) + + +;;; {Symbol Properties} +;;; + +(define (symbol-property sym prop) + (let ((pair (assoc prop (symbol-pref sym)))) + (and pair (cdr pair)))) + +(define (set-symbol-property! sym prop val) + (let ((pair (assoc prop (symbol-pref sym)))) + (if pair + (set-cdr! pair val) + (symbol-pset! sym (acons prop val (symbol-pref sym)))))) + +(define (symbol-property-remove! sym prop) + (let ((pair (assoc prop (symbol-pref sym)))) + (if pair + (symbol-pset! sym (delq! pair (symbol-pref sym)))))) + +;;; {General Properties} +;;; + +;; This is a more modern interface to properties. It will replace all +;; other property-like things eventually. + +(define (make-object-property) + (let ((prop (primitive-make-property #f))) + (make-procedure-with-setter + (lambda (obj) (primitive-property-ref prop obj)) + (lambda (obj val) (primitive-property-set! prop obj val))))) + + + +;;; {Arrays} +;;; + +(if (provided? 'array) + (primitive-load-path "ice-9/arrays.scm")) + + +;;; {Keywords} +;;; + +(define (symbol->keyword symbol) + (make-keyword-from-dash-symbol (symbol-append '- symbol))) + +(define (keyword->symbol kw) + (let ((sym (symbol->string (keyword-dash-symbol kw)))) + (string->symbol (substring sym 1 (string-length sym))))) + +(define (kw-arg-ref args kw) + (let ((rem (member kw args))) + (and rem (pair? (cdr rem)) (cadr rem)))) + + + +;;; {Structs} + +(define (struct-layout s) + (struct-ref (struct-vtable s) vtable-index-layout)) + + + +;;; Environments + +(define the-environment + (procedure->syntax + (lambda (x e) + e))) + +(define the-root-environment (the-environment)) + +(define (environment-module env) + (let ((closure (and (pair? env) (car (last-pair env))))) + (and closure (procedure-property closure 'module)))) + + +;;; {Records} +;;; + +;; Printing records: by default, records are printed as +;; +;; # +;; +;; You can change that by giving a custom printing function to +;; MAKE-RECORD-TYPE (after the list of field symbols). This function +;; will be called like +;; +;; ( object port) +;; +;; It should print OBJECT to PORT. + +(define (inherit-print-state old-port new-port) + (if (get-print-state old-port) + (port-with-print-state new-port (get-print-state old-port)) + new-port)) + +;; 0: type-name, 1: fields +(define record-type-vtable + (make-vtable-vtable "prpr" 0 + (lambda (s p) + (cond ((eq? s record-type-vtable) + (display "#" p)) + (else + (display "#" p)))))) + +(define (record-type? obj) + (and (struct? obj) (eq? record-type-vtable (struct-vtable obj)))) + +(define (make-record-type type-name fields . opt) + (let ((printer-fn (and (pair? opt) (car opt)))) + (let ((struct (make-struct record-type-vtable 0 + (make-struct-layout + (apply string-append + (map (lambda (f) "pw") fields))) + (or printer-fn + (lambda (s p) + (display "#<" p) + (display type-name p) + (let loop ((fields fields) + (off 0)) + (cond + ((not (null? fields)) + (display " " p) + (display (car fields) p) + (display ": " p) + (display (struct-ref s off) p) + (loop (cdr fields) (+ 1 off))))) + (display ">" p))) + type-name + (copy-tree fields)))) + ;; Temporary solution: Associate a name to the record type descriptor + ;; so that the object system can create a wrapper class for it. + (set-struct-vtable-name! struct (if (symbol? type-name) + type-name + (string->symbol type-name))) + struct))) + +(define (record-type-name obj) + (if (record-type? obj) + (struct-ref obj vtable-offset-user) + (error 'not-a-record-type obj))) + +(define (record-type-fields obj) + (if (record-type? obj) + (struct-ref obj (+ 1 vtable-offset-user)) + (error 'not-a-record-type obj))) + +(define (record-constructor rtd . opt) + (let ((field-names (if (pair? opt) (car opt) (record-type-fields rtd)))) + (local-eval `(lambda ,field-names + (make-struct ',rtd 0 ,@(map (lambda (f) + (if (memq f field-names) + f + #f)) + (record-type-fields rtd)))) + the-root-environment))) + +(define (record-predicate rtd) + (lambda (obj) (and (struct? obj) (eq? rtd (struct-vtable obj))))) + +(define (record-accessor rtd field-name) + (let* ((pos (list-index (record-type-fields rtd) field-name))) + (if (not pos) + (error 'no-such-field field-name)) + (local-eval `(lambda (obj) + (and (eq? ',rtd (record-type-descriptor obj)) + (struct-ref obj ,pos))) + the-root-environment))) + +(define (record-modifier rtd field-name) + (let* ((pos (list-index (record-type-fields rtd) field-name))) + (if (not pos) + (error 'no-such-field field-name)) + (local-eval `(lambda (obj val) + (and (eq? ',rtd (record-type-descriptor obj)) + (struct-set! obj ,pos val))) + the-root-environment))) + + +(define (record? obj) + (and (struct? obj) (record-type? (struct-vtable obj)))) + +(define (record-type-descriptor obj) + (if (struct? obj) + (struct-vtable obj) + (error 'not-a-record obj))) + +(provide 'record) + + +;;; {Booleans} +;;; + +(define (->bool x) (not (not x))) + + +;;; {Symbols} +;;; + +(define (symbol-append . args) + (string->symbol (apply string-append (map symbol->string args)))) + +(define (list->symbol . args) + (string->symbol (apply list->string args))) + +(define (symbol . args) + (string->symbol (apply string args))) + + +;;; {Lists} +;;; + +(define (list-index l k) + (let loop ((n 0) + (l l)) + (and (not (null? l)) + (if (eq? (car l) k) + n + (loop (+ n 1) (cdr l)))))) + +(define (make-list n . init) + (if (pair? init) (set! init (car init))) + (let loop ((answer '()) + (n n)) + (if (<= n 0) + answer + (loop (cons init answer) (- n 1))))) + + +;;; {and-map and or-map} +;;; +;;; (and-map fn lst) is like (and (fn (car lst)) (fn (cadr lst)) (fn...) ...) +;;; (or-map fn lst) is like (or (fn (car lst)) (fn (cadr lst)) (fn...) ...) +;;; + +;; and-map f l +;; +;; Apply f to successive elements of l until exhaustion or f returns #f. +;; If returning early, return #f. Otherwise, return the last value returned +;; by f. If f has never been called because l is empty, return #t. +;; +(define (and-map f lst) + (let loop ((result #t) + (l lst)) + (and result + (or (and (null? l) + result) + (loop (f (car l)) (cdr l)))))) + +;; or-map f l +;; +;; Apply f to successive elements of l until exhaustion or while f returns #f. +;; If returning early, return the return value of f. +;; +(define (or-map f lst) + (let loop ((result #f) + (l lst)) + (or result + (and (not (null? l)) + (loop (f (car l)) (cdr l)))))) + + + +(if (provided? 'posix) + (primitive-load-path "ice-9/posix.scm")) + +(if (provided? 'socket) + (primitive-load-path "ice-9/networking.scm")) + +(define file-exists? + (if (provided? 'posix) + (lambda (str) + (->bool (false-if-exception (stat str)))) + (lambda (str) + (let ((port (catch 'system-error (lambda () (open-file str OPEN_READ)) + (lambda args #f)))) + (if port (begin (close-port port) #t) + #f))))) + +(define file-is-directory? + (if (provided? 'posix) + (lambda (str) + (eq? (stat:type (stat str)) 'directory)) + (lambda (str) + (let ((port (catch 'system-error + (lambda () (open-file (string-append str "/.") + OPEN_READ)) + (lambda args #f)))) + (if port (begin (close-port port) #t) + #f))))) + +(define (has-suffix? str suffix) + (let ((sufl (string-length suffix)) + (sl (string-length str))) + (and (> sl sufl) + (string=? (substring str (- sl sufl) sl) suffix)))) + +(define (system-error-errno args) + (if (eq? (car args) 'system-error) + (car (list-ref args 4)) + #f)) + + +;;; {Error Handling} +;;; + +(define (error . args) + (save-stack) + (if (null? args) + (scm-error 'misc-error #f "?" #f #f) + (let loop ((msg "~A") + (rest (cdr args))) + (if (not (null? rest)) + (loop (string-append msg " ~S") + (cdr rest)) + (scm-error 'misc-error #f msg args #f))))) + +;; bad-throw is the hook that is called upon a throw to a an unhandled +;; key (unless the throw has four arguments, in which case +;; it's usually interpreted as an error throw.) +;; If the key has a default handler (a throw-handler-default property), +;; it is applied to the throw. +;; +(define (bad-throw key . args) + (let ((default (symbol-property key 'throw-handler-default))) + (or (and default (apply default key args)) + (apply error "unhandled-exception:" key args)))) + + + +(define (tm:sec obj) (vector-ref obj 0)) +(define (tm:min obj) (vector-ref obj 1)) +(define (tm:hour obj) (vector-ref obj 2)) +(define (tm:mday obj) (vector-ref obj 3)) +(define (tm:mon obj) (vector-ref obj 4)) +(define (tm:year obj) (vector-ref obj 5)) +(define (tm:wday obj) (vector-ref obj 6)) +(define (tm:yday obj) (vector-ref obj 7)) +(define (tm:isdst obj) (vector-ref obj 8)) +(define (tm:gmtoff obj) (vector-ref obj 9)) +(define (tm:zone obj) (vector-ref obj 10)) + +(define (set-tm:sec obj val) (vector-set! obj 0 val)) +(define (set-tm:min obj val) (vector-set! obj 1 val)) +(define (set-tm:hour obj val) (vector-set! obj 2 val)) +(define (set-tm:mday obj val) (vector-set! obj 3 val)) +(define (set-tm:mon obj val) (vector-set! obj 4 val)) +(define (set-tm:year obj val) (vector-set! obj 5 val)) +(define (set-tm:wday obj val) (vector-set! obj 6 val)) +(define (set-tm:yday obj val) (vector-set! obj 7 val)) +(define (set-tm:isdst obj val) (vector-set! obj 8 val)) +(define (set-tm:gmtoff obj val) (vector-set! obj 9 val)) +(define (set-tm:zone obj val) (vector-set! obj 10 val)) + +(define (tms:clock obj) (vector-ref obj 0)) +(define (tms:utime obj) (vector-ref obj 1)) +(define (tms:stime obj) (vector-ref obj 2)) +(define (tms:cutime obj) (vector-ref obj 3)) +(define (tms:cstime obj) (vector-ref obj 4)) + +(define file-position ftell) +(define (file-set-position port offset . whence) + (let ((whence (if (eq? whence '()) SEEK_SET (car whence)))) + (seek port offset whence))) + +(define (move->fdes fd/port fd) + (cond ((integer? fd/port) + (dup->fdes fd/port fd) + (close fd/port) + fd) + (else + (primitive-move->fdes fd/port fd) + (set-port-revealed! fd/port 1) + fd/port))) + +(define (release-port-handle port) + (let ((revealed (port-revealed port))) + (if (> revealed 0) + (set-port-revealed! port (- revealed 1))))) + +(define (dup->port port/fd mode . maybe-fd) + (let ((port (fdopen (apply dup->fdes port/fd maybe-fd) + mode))) + (if (pair? maybe-fd) + (set-port-revealed! port 1)) + port)) + +(define (dup->inport port/fd . maybe-fd) + (apply dup->port port/fd "r" maybe-fd)) + +(define (dup->outport port/fd . maybe-fd) + (apply dup->port port/fd "w" maybe-fd)) + +(define (dup port/fd . maybe-fd) + (if (integer? port/fd) + (apply dup->fdes port/fd maybe-fd) + (apply dup->port port/fd (port-mode port/fd) maybe-fd))) + +(define (duplicate-port port modes) + (dup->port port modes)) + +(define (fdes->inport fdes) + (let loop ((rest-ports (fdes->ports fdes))) + (cond ((null? rest-ports) + (let ((result (fdopen fdes "r"))) + (set-port-revealed! result 1) + result)) + ((input-port? (car rest-ports)) + (set-port-revealed! (car rest-ports) + (+ (port-revealed (car rest-ports)) 1)) + (car rest-ports)) + (else + (loop (cdr rest-ports)))))) + +(define (fdes->outport fdes) + (let loop ((rest-ports (fdes->ports fdes))) + (cond ((null? rest-ports) + (let ((result (fdopen fdes "w"))) + (set-port-revealed! result 1) + result)) + ((output-port? (car rest-ports)) + (set-port-revealed! (car rest-ports) + (+ (port-revealed (car rest-ports)) 1)) + (car rest-ports)) + (else + (loop (cdr rest-ports)))))) + +(define (port->fdes port) + (set-port-revealed! port (+ (port-revealed port) 1)) + (fileno port)) + +(define (setenv name value) + (if value + (putenv (string-append name "=" value)) + (putenv name))) + + +;;; {Load Paths} +;;; + +;;; Here for backward compatability +;; +(define scheme-file-suffix (lambda () ".scm")) + +(define (in-vicinity vicinity file) + (let ((tail (let ((len (string-length vicinity))) + (if (zero? len) + #f + (string-ref vicinity (- len 1)))))) + (string-append vicinity + (if (or (not tail) + (eq? tail #\/)) + "" + "/") + file))) + + +;;; {Help for scm_shell} +;;; The argument-processing code used by Guile-based shells generates +;;; Scheme code based on the argument list. This page contains help +;;; functions for the code it generates. + +(define (command-line) (program-arguments)) + +;; This is mostly for the internal use of the code generated by +;; scm_compile_shell_switches. +(define (load-user-init) + (let* ((home (or (getenv "HOME") + (false-if-exception (passwd:dir (getpwuid (getuid)))) + "/")) ;; fallback for cygwin etc. + (init-file (in-vicinity home ".guile"))) + (if (file-exists? init-file) + (primitive-load init-file)))) + + +;;; {Loading by paths} + +;;; Load a Scheme source file named NAME, searching for it in the +;;; directories listed in %load-path, and applying each of the file +;;; name extensions listed in %load-extensions. +(define (load-from-path name) + (start-stack 'load-stack + (primitive-load-path name))) + + + +;;; {Transcendental Functions} +;;; +;;; Derived from "Transcen.scm", Complex trancendental functions for SCM. +;;; Written by Jerry D. Hedden, (C) FSF. +;;; See the file `COPYING' for terms applying to this program. +;;; + +(define (exp z) + (if (real? z) ($exp z) + (make-polar ($exp (real-part z)) (imag-part z)))) + +(define (log z) + (if (and (real? z) (>= z 0)) + ($log z) + (make-rectangular ($log (magnitude z)) (angle z)))) + +(define (sqrt z) + (if (real? z) + (if (negative? z) (make-rectangular 0 ($sqrt (- z))) + ($sqrt z)) + (make-polar ($sqrt (magnitude z)) (/ (angle z) 2)))) + +(define expt + (let ((integer-expt integer-expt)) + (lambda (z1 z2) + (cond ((integer? z2) + (if (negative? z2) + (/ 1 (integer-expt z1 (- z2))) + (integer-expt z1 z2))) + ((and (real? z2) (real? z1) (>= z1 0)) + ($expt z1 z2)) + (else + (exp (* z2 (log z1)))))))) + +(define (sinh z) + (if (real? z) ($sinh z) + (let ((x (real-part z)) (y (imag-part z))) + (make-rectangular (* ($sinh x) ($cos y)) + (* ($cosh x) ($sin y)))))) +(define (cosh z) + (if (real? z) ($cosh z) + (let ((x (real-part z)) (y (imag-part z))) + (make-rectangular (* ($cosh x) ($cos y)) + (* ($sinh x) ($sin y)))))) +(define (tanh z) + (if (real? z) ($tanh z) + (let* ((x (* 2 (real-part z))) + (y (* 2 (imag-part z))) + (w (+ ($cosh x) ($cos y)))) + (make-rectangular (/ ($sinh x) w) (/ ($sin y) w))))) + +(define (asinh z) + (if (real? z) ($asinh z) + (log (+ z (sqrt (+ (* z z) 1)))))) + +(define (acosh z) + (if (and (real? z) (>= z 1)) + ($acosh z) + (log (+ z (sqrt (- (* z z) 1)))))) + +(define (atanh z) + (if (and (real? z) (> z -1) (< z 1)) + ($atanh z) + (/ (log (/ (+ 1 z) (- 1 z))) 2))) + +(define (sin z) + (if (real? z) ($sin z) + (let ((x (real-part z)) (y (imag-part z))) + (make-rectangular (* ($sin x) ($cosh y)) + (* ($cos x) ($sinh y)))))) +(define (cos z) + (if (real? z) ($cos z) + (let ((x (real-part z)) (y (imag-part z))) + (make-rectangular (* ($cos x) ($cosh y)) + (- (* ($sin x) ($sinh y))))))) +(define (tan z) + (if (real? z) ($tan z) + (let* ((x (* 2 (real-part z))) + (y (* 2 (imag-part z))) + (w (+ ($cos x) ($cosh y)))) + (make-rectangular (/ ($sin x) w) (/ ($sinh y) w))))) + +(define (asin z) + (if (and (real? z) (>= z -1) (<= z 1)) + ($asin z) + (* -i (asinh (* +i z))))) + +(define (acos z) + (if (and (real? z) (>= z -1) (<= z 1)) + ($acos z) + (+ (/ (angle -1) 2) (* +i (asinh (* +i z)))))) + +(define (atan z . y) + (if (null? y) + (if (real? z) ($atan z) + (/ (log (/ (- +i z) (+ +i z))) +2i)) + ($atan2 z (car y)))) + +(define (log10 arg) + (/ (log arg) (log 10))) + + + +;;; {Reader Extensions} +;;; + +;;; Reader code for various "#c" forms. +;;; + +(read-hash-extend #\' (lambda (c port) + (read port))) + +(define read-eval? (make-fluid)) +(fluid-set! read-eval? #f) +(read-hash-extend #\. + (lambda (c port) + (if (fluid-ref read-eval?) + (eval (read port) (interaction-environment)) + (error + "#. read expansion found and read-eval? is #f.")))) + + +;;; {Command Line Options} +;;; + +(define (get-option argv kw-opts kw-args return) + (cond + ((null? argv) + (return #f #f argv)) + + ((or (not (eq? #\- (string-ref (car argv) 0))) + (eq? (string-length (car argv)) 1)) + (return 'normal-arg (car argv) (cdr argv))) + + ((eq? #\- (string-ref (car argv) 1)) + (let* ((kw-arg-pos (or (string-index (car argv) #\=) + (string-length (car argv)))) + (kw (symbol->keyword (substring (car argv) 2 kw-arg-pos))) + (kw-opt? (member kw kw-opts)) + (kw-arg? (member kw kw-args)) + (arg (or (and (not (eq? kw-arg-pos (string-length (car argv)))) + (substring (car argv) + (+ kw-arg-pos 1) + (string-length (car argv)))) + (and kw-arg? + (begin (set! argv (cdr argv)) (car argv)))))) + (if (or kw-opt? kw-arg?) + (return kw arg (cdr argv)) + (return 'usage-error kw (cdr argv))))) + + (else + (let* ((char (substring (car argv) 1 2)) + (kw (symbol->keyword char))) + (cond + + ((member kw kw-opts) + (let* ((rest-car (substring (car argv) 2 (string-length (car argv)))) + (new-argv (if (= 0 (string-length rest-car)) + (cdr argv) + (cons (string-append "-" rest-car) (cdr argv))))) + (return kw #f new-argv))) + + ((member kw kw-args) + (let* ((rest-car (substring (car argv) 2 (string-length (car argv)))) + (arg (if (= 0 (string-length rest-car)) + (cadr argv) + rest-car)) + (new-argv (if (= 0 (string-length rest-car)) + (cddr argv) + (cdr argv)))) + (return kw arg new-argv))) + + (else (return 'usage-error kw argv))))))) + +(define (for-next-option proc argv kw-opts kw-args) + (let loop ((argv argv)) + (get-option argv kw-opts kw-args + (lambda (opt opt-arg argv) + (and opt (proc opt opt-arg argv loop)))))) + +(define (display-usage-report kw-desc) + (for-each + (lambda (kw) + (or (eq? (car kw) #t) + (eq? (car kw) 'else) + (let* ((opt-desc kw) + (help (cadr opt-desc)) + (opts (car opt-desc)) + (opts-proper (if (string? (car opts)) (cdr opts) opts)) + (arg-name (if (string? (car opts)) + (string-append "<" (car opts) ">") + "")) + (left-part (string-append + (with-output-to-string + (lambda () + (map (lambda (x) (display (keyword->symbol x)) (display " ")) + opts-proper))) + arg-name)) + (middle-part (if (and (< (string-length left-part) 30) + (< (string-length help) 40)) + (make-string (- 30 (string-length left-part)) #\ ) + "\n\t"))) + (display left-part) + (display middle-part) + (display help) + (newline)))) + kw-desc)) + + + +(define (transform-usage-lambda cases) + (let* ((raw-usage (delq! 'else (map car cases))) + (usage-sans-specials (map (lambda (x) + (or (and (not (list? x)) x) + (and (symbol? (car x)) #t) + (and (boolean? (car x)) #t) + x)) + raw-usage)) + (usage-desc (delq! #t usage-sans-specials)) + (kw-desc (map car usage-desc)) + (kw-opts (apply append (map (lambda (x) (and (not (string? (car x))) x)) kw-desc))) + (kw-args (apply append (map (lambda (x) (and (string? (car x)) (cdr x))) kw-desc))) + (transmogrified-cases (map (lambda (case) + (cons (let ((opts (car case))) + (if (or (boolean? opts) (eq? 'else opts)) + opts + (cond + ((symbol? (car opts)) opts) + ((boolean? (car opts)) opts) + ((string? (caar opts)) (cdar opts)) + (else (car opts))))) + (cdr case))) + cases))) + `(let ((%display-usage (lambda () (display-usage-report ',usage-desc)))) + (lambda (%argv) + (let %next-arg ((%argv %argv)) + (get-option %argv + ',kw-opts + ',kw-args + (lambda (%opt %arg %new-argv) + (case %opt + ,@ transmogrified-cases)))))))) + + + + +;;; {Low Level Modules} +;;; +;;; These are the low level data structures for modules. +;;; +;;; !!! warning: The interface to lazy binder procedures is going +;;; to be changed in an incompatible way to permit all the basic +;;; module ops to be virtualized. +;;; +;;; (make-module size use-list lazy-binding-proc) => module +;;; module-{obarray,uses,binder}[|-set!] +;;; (module? obj) => [#t|#f] +;;; (module-locally-bound? module symbol) => [#t|#f] +;;; (module-bound? module symbol) => [#t|#f] +;;; (module-symbol-locally-interned? module symbol) => [#t|#f] +;;; (module-symbol-interned? module symbol) => [#t|#f] +;;; (module-local-variable module symbol) => [# | #f] +;;; (module-variable module symbol) => [# | #f] +;;; (module-symbol-binding module symbol opt-value) +;;; => [ | opt-value | an error occurs ] +;;; (module-make-local-var! module symbol) => # +;;; (module-add! module symbol var) => unspecified +;;; (module-remove! module symbol) => unspecified +;;; (module-for-each proc module) => unspecified +;;; (make-scm-module) => module ; a lazy copy of the symhash module +;;; (set-current-module module) => unspecified +;;; (current-module) => # +;;; +;;; + + +;;; {Printing Modules} +;; This is how modules are printed. You can re-define it. +;; (Redefining is actually more complicated than simply redefining +;; %print-module because that would only change the binding and not +;; the value stored in the vtable that determines how record are +;; printed. Sigh.) + +(define (%print-module mod port) ; unused args: depth length style table) + (display "#<" port) + (display (or (module-kind mod) "module") port) + (let ((name (module-name mod))) + (if name + (begin + (display " " port) + (display name port)))) + (display " " port) + (display (number->string (object-address mod) 16) port) + (display ">" port)) + +;; module-type +;; +;; A module is characterized by an obarray in which local symbols +;; are interned, a list of modules, "uses", from which non-local +;; bindings can be inherited, and an optional lazy-binder which +;; is a (CLOSURE module symbol) which, as a last resort, can provide +;; bindings that would otherwise not be found locally in the module. +;; +;; NOTE: If you change here, you also need to change libguile/modules.h. +;; +(define module-type + (make-record-type 'module + '(obarray uses binder eval-closure transformer name kind + observers weak-observers observer-id) + %print-module)) + +;; make-module &opt size uses binder +;; +;; Create a new module, perhaps with a particular size of obarray, +;; initial uses list, or binding procedure. +;; +(define make-module + (lambda args + + (define (parse-arg index default) + (if (> (length args) index) + (list-ref args index) + default)) + + (if (> (length args) 3) + (error "Too many args to make-module." args)) + + (let ((size (parse-arg 0 1021)) + (uses (parse-arg 1 '())) + (binder (parse-arg 2 #f))) + + (if (not (integer? size)) + (error "Illegal size to make-module." size)) + (if (not (and (list? uses) + (and-map module? uses))) + (error "Incorrect use list." uses)) + (if (and binder (not (procedure? binder))) + (error + "Lazy-binder expected to be a procedure or #f." binder)) + + (let ((module (module-constructor (make-vector size '()) + uses binder #f #f #f #f + '() + (make-weak-value-hash-table 31) + 0))) + + ;; We can't pass this as an argument to module-constructor, + ;; because we need it to close over a pointer to the module + ;; itself. + (set-module-eval-closure! module (standard-eval-closure module)) + + module)))) + +(define module-constructor (record-constructor module-type)) +(define module-obarray (record-accessor module-type 'obarray)) +(define set-module-obarray! (record-modifier module-type 'obarray)) +(define module-uses (record-accessor module-type 'uses)) +(define set-module-uses! (record-modifier module-type 'uses)) +(define module-binder (record-accessor module-type 'binder)) +(define set-module-binder! (record-modifier module-type 'binder)) + +;; NOTE: This binding is used in libguile/modules.c. +(define module-eval-closure (record-accessor module-type 'eval-closure)) + +(define module-transformer (record-accessor module-type 'transformer)) +(define set-module-transformer! (record-modifier module-type 'transformer)) +(define module-name (record-accessor module-type 'name)) +(define set-module-name! (record-modifier module-type 'name)) +(define module-kind (record-accessor module-type 'kind)) +(define set-module-kind! (record-modifier module-type 'kind)) +(define module-observers (record-accessor module-type 'observers)) +(define set-module-observers! (record-modifier module-type 'observers)) +(define module-weak-observers (record-accessor module-type 'weak-observers)) +(define module-observer-id (record-accessor module-type 'observer-id)) +(define set-module-observer-id! (record-modifier module-type 'observer-id)) +(define module? (record-predicate module-type)) + +(define set-module-eval-closure! + (let ((setter (record-modifier module-type 'eval-closure))) + (lambda (module closure) + (setter module closure) + ;; Make it possible to lookup the module from the environment. + ;; This implementation is correct since an eval closure can belong + ;; to maximally one module. + (set-procedure-property! closure 'module module)))) + +(begin-deprecated + (define (eval-in-module exp mod) + (issue-deprecation-warning + "`eval-in-module' is deprecated. Use `eval' instead.") + (eval exp mod))) + + +;;; {Observer protocol} +;;; + +(define (module-observe module proc) + (set-module-observers! module (cons proc (module-observers module))) + (cons module proc)) + +(define (module-observe-weak module proc) + (let ((id (module-observer-id module))) + (hash-set! (module-weak-observers module) id proc) + (set-module-observer-id! module (+ 1 id)) + (cons module id))) + +(define (module-unobserve token) + (let ((module (car token)) + (id (cdr token))) + (if (integer? id) + (hash-remove! (module-weak-observers module) id) + (set-module-observers! module (delq1! id (module-observers module))))) + *unspecified*) + +(define (module-modified m) + (for-each (lambda (proc) (proc m)) (module-observers m)) + (hash-fold (lambda (id proc res) (proc m)) #f (module-weak-observers m))) + + +;;; {Module Searching in General} +;;; +;;; We sometimes want to look for properties of a symbol +;;; just within the obarray of one module. If the property +;;; holds, then it is said to hold ``locally'' as in, ``The symbol +;;; DISPLAY is locally rebound in the module `safe-guile'.'' +;;; +;;; +;;; Other times, we want to test for a symbol property in the obarray +;;; of M and, if it is not found there, try each of the modules in the +;;; uses list of M. This is the normal way of testing for some +;;; property, so we state these properties without qualification as +;;; in: ``The symbol 'fnord is interned in module M because it is +;;; interned locally in module M2 which is a member of the uses list +;;; of M.'' +;;; + +;; module-search fn m +;; +;; return the first non-#f result of FN applied to M and then to +;; the modules in the uses of m, and so on recursively. If all applications +;; return #f, then so does this function. +;; +(define (module-search fn m v) + (define (loop pos) + (and (pair? pos) + (or (module-search fn (car pos) v) + (loop (cdr pos))))) + (or (fn m v) + (loop (module-uses m)))) + + +;;; {Is a symbol bound in a module?} +;;; +;;; Symbol S in Module M is bound if S is interned in M and if the binding +;;; of S in M has been set to some well-defined value. +;;; + +;; module-locally-bound? module symbol +;; +;; Is a symbol bound (interned and defined) locally in a given module? +;; +(define (module-locally-bound? m v) + (let ((var (module-local-variable m v))) + (and var + (variable-bound? var)))) + +;; module-bound? module symbol +;; +;; Is a symbol bound (interned and defined) anywhere in a given module +;; or its uses? +;; +(define (module-bound? m v) + (module-search module-locally-bound? m v)) + +;;; {Is a symbol interned in a module?} +;;; +;;; Symbol S in Module M is interned if S occurs in +;;; of S in M has been set to some well-defined value. +;;; +;;; It is possible to intern a symbol in a module without providing +;;; an initial binding for the corresponding variable. This is done +;;; with: +;;; (module-add! module symbol (make-undefined-variable)) +;;; +;;; In that case, the symbol is interned in the module, but not +;;; bound there. The unbound symbol shadows any binding for that +;;; symbol that might otherwise be inherited from a member of the uses list. +;;; + +(define (module-obarray-get-handle ob key) + ((if (symbol? key) hashq-get-handle hash-get-handle) ob key)) + +(define (module-obarray-ref ob key) + ((if (symbol? key) hashq-ref hash-ref) ob key)) + +(define (module-obarray-set! ob key val) + ((if (symbol? key) hashq-set! hash-set!) ob key val)) + +(define (module-obarray-remove! ob key) + ((if (symbol? key) hashq-remove! hash-remove!) ob key)) + +;; module-symbol-locally-interned? module symbol +;; +;; is a symbol interned (not neccessarily defined) locally in a given module +;; or its uses? Interned symbols shadow inherited bindings even if +;; they are not themselves bound to a defined value. +;; +(define (module-symbol-locally-interned? m v) + (not (not (module-obarray-get-handle (module-obarray m) v)))) + +;; module-symbol-interned? module symbol +;; +;; is a symbol interned (not neccessarily defined) anywhere in a given module +;; or its uses? Interned symbols shadow inherited bindings even if +;; they are not themselves bound to a defined value. +;; +(define (module-symbol-interned? m v) + (module-search module-symbol-locally-interned? m v)) + + +;;; {Mapping modules x symbols --> variables} +;;; + +;; module-local-variable module symbol +;; return the local variable associated with a MODULE and SYMBOL. +;; +;;; This function is very important. It is the only function that can +;;; return a variable from a module other than the mutators that store +;;; new variables in modules. Therefore, this function is the location +;;; of the "lazy binder" hack. +;;; +;;; If symbol is defined in MODULE, and if the definition binds symbol +;;; to a variable, return that variable object. +;;; +;;; If the symbols is not found at first, but the module has a lazy binder, +;;; then try the binder. +;;; +;;; If the symbol is not found at all, return #f. +;;; +(define (module-local-variable m v) +; (caddr +; (list m v + (let ((b (module-obarray-ref (module-obarray m) v))) + (or (and (variable? b) b) + (and (module-binder m) + ((module-binder m) m v #f))))) +;)) + +;; module-variable module symbol +;; +;; like module-local-variable, except search the uses in the +;; case V is not found in M. +;; +;; NOTE: This function is superseded with C code (see modules.c) +;;; when using the standard eval closure. +;; +(define (module-variable m v) + (module-search module-local-variable m v)) + + +;;; {Mapping modules x symbols --> bindings} +;;; +;;; These are similar to the mapping to variables, except that the +;;; variable is dereferenced. +;;; + +;; module-symbol-binding module symbol opt-value +;; +;; return the binding of a variable specified by name within +;; a given module, signalling an error if the variable is unbound. +;; If the OPT-VALUE is passed, then instead of signalling an error, +;; return OPT-VALUE. +;; +(define (module-symbol-local-binding m v . opt-val) + (let ((var (module-local-variable m v))) + (if var + (variable-ref var) + (if (not (null? opt-val)) + (car opt-val) + (error "Locally unbound variable." v))))) + +;; module-symbol-binding module symbol opt-value +;; +;; return the binding of a variable specified by name within +;; a given module, signalling an error if the variable is unbound. +;; If the OPT-VALUE is passed, then instead of signalling an error, +;; return OPT-VALUE. +;; +(define (module-symbol-binding m v . opt-val) + (let ((var (module-variable m v))) + (if var + (variable-ref var) + (if (not (null? opt-val)) + (car opt-val) + (error "Unbound variable." v))))) + + + +;;; {Adding Variables to Modules} +;;; +;;; + + +;; module-make-local-var! module symbol +;; +;; ensure a variable for V in the local namespace of M. +;; If no variable was already there, then create a new and uninitialzied +;; variable. +;; +(define (module-make-local-var! m v) + (or (let ((b (module-obarray-ref (module-obarray m) v))) + (and (variable? b) + (begin + (module-modified m) + b))) + (and (module-binder m) + ((module-binder m) m v #t)) + (begin + (let ((answer (make-undefined-variable))) + (variable-set-name-hint! answer v) + (module-obarray-set! (module-obarray m) v answer) + (module-modified m) + answer)))) + +;; module-ensure-local-variable! module symbol +;; +;; Ensure that there is a local variable in MODULE for SYMBOL. If +;; there is no binding for SYMBOL, create a new uninitialized +;; variable. Return the local variable. +;; +(define (module-ensure-local-variable! module symbol) + (or (module-local-variable module symbol) + (let ((var (make-undefined-variable))) + (variable-set-name-hint! var symbol) + (module-add! module symbol var) + var))) + +;; module-add! module symbol var +;; +;; ensure a particular variable for V in the local namespace of M. +;; +(define (module-add! m v var) + (if (not (variable? var)) + (error "Bad variable to module-add!" var)) + (module-obarray-set! (module-obarray m) v var) + (module-modified m)) + +;; module-remove! +;; +;; make sure that a symbol is undefined in the local namespace of M. +;; +(define (module-remove! m v) + (module-obarray-remove! (module-obarray m) v) + (module-modified m)) + +(define (module-clear! m) + (vector-fill! (module-obarray m) '()) + (module-modified m)) + +;; MODULE-FOR-EACH -- exported +;; +;; Call PROC on each symbol in MODULE, with arguments of (SYMBOL VARIABLE). +;; +(define (module-for-each proc module) + (let ((obarray (module-obarray module))) + (do ((index 0 (+ index 1)) + (end (vector-length obarray))) + ((= index end)) + (for-each + (lambda (bucket) + (proc (car bucket) (cdr bucket))) + (vector-ref obarray index))))) + + +(define (module-map proc module) + (let* ((obarray (module-obarray module)) + (end (vector-length obarray))) + + (let loop ((i 0) + (answer '())) + (if (= i end) + answer + (loop (+ 1 i) + (append! + (map (lambda (bucket) + (proc (car bucket) (cdr bucket))) + (vector-ref obarray i)) + answer)))))) + + +;;; {Low Level Bootstrapping} +;;; + +;; make-root-module + +;; A root module uses the pre-modules-obarray as its obarray. This +;; special obarray accumulates all bindings that have been established +;; before the module system is fully booted. +;; +;; (The obarray continues to be used by code that has been closed over +;; before the module system has been booted.) + +(define (make-root-module) + (let ((m (make-module 0))) + (set-module-obarray! m (%get-pre-modules-obarray)) + m)) + +;; make-scm-module + +;; The root interface is a module that uses the same obarray as the +;; root module. It does not allow new definitions, tho. + +(define (make-scm-module) + (let ((m (make-module 0))) + (set-module-obarray! m (%get-pre-modules-obarray)) + (set-module-eval-closure! m (standard-interface-eval-closure m)) + m)) + + + +;;; {Module-based Loading} +;;; + +(define (save-module-excursion thunk) + (let ((inner-module (current-module)) + (outer-module #f)) + (dynamic-wind (lambda () + (set! outer-module (current-module)) + (set-current-module inner-module) + (set! inner-module #f)) + thunk + (lambda () + (set! inner-module (current-module)) + (set-current-module outer-module) + (set! outer-module #f))))) + +(define basic-load load) + +(define (load-module filename) + (save-module-excursion + (lambda () + (let ((oldname (and (current-load-port) + (port-filename (current-load-port))))) + (basic-load (if (and oldname + (> (string-length filename) 0) + (not (char=? (string-ref filename 0) #\/)) + (not (string=? (dirname oldname) "."))) + (string-append (dirname oldname) "/" filename) + filename)))))) + + + +;;; {MODULE-REF -- exported} +;; +;; Returns the value of a variable called NAME in MODULE or any of its +;; used modules. If there is no such variable, then if the optional third +;; argument DEFAULT is present, it is returned; otherwise an error is signaled. +;; +(define (module-ref module name . rest) + (let ((variable (module-variable module name))) + (if (and variable (variable-bound? variable)) + (variable-ref variable) + (if (null? rest) + (error "No variable named" name 'in module) + (car rest) ; default value + )))) + +;; MODULE-SET! -- exported +;; +;; Sets the variable called NAME in MODULE (or in a module that MODULE uses) +;; to VALUE; if there is no such variable, an error is signaled. +;; +(define (module-set! module name value) + (let ((variable (module-variable module name))) + (if variable + (variable-set! variable value) + (error "No variable named" name 'in module)))) + +;; MODULE-DEFINE! -- exported +;; +;; Sets the variable called NAME in MODULE to VALUE; if there is no such +;; variable, it is added first. +;; +(define (module-define! module name value) + (let ((variable (module-local-variable module name))) + (if variable + (begin + (variable-set! variable value) + (module-modified module)) + (let ((variable (make-variable value))) + (variable-set-name-hint! variable name) + (module-add! module name variable))))) + +;; MODULE-DEFINED? -- exported +;; +;; Return #t iff NAME is defined in MODULE (or in a module that MODULE +;; uses) +;; +(define (module-defined? module name) + (let ((variable (module-variable module name))) + (and variable (variable-bound? variable)))) + +;; MODULE-USE! module interface +;; +;; Add INTERFACE to the list of interfaces used by MODULE. +;; +(define (module-use! module interface) + (set-module-uses! module + (cons interface (delq! interface (module-uses module)))) + (module-modified module)) + + +;;; {Recursive Namespaces} +;;; +;;; +;;; A hierarchical namespace emerges if we consider some module to be +;;; root, and variables bound to modules as nested namespaces. +;;; +;;; The routines in this file manage variable names in hierarchical namespace. +;;; Each variable name is a list of elements, looked up in successively nested +;;; modules. +;;; +;;; (nested-ref some-root-module '(foo bar baz)) +;;; => +;;; +;;; +;;; There are: +;;; +;;; ;; a-root is a module +;;; ;; name is a list of symbols +;;; +;;; nested-ref a-root name +;;; nested-set! a-root name val +;;; nested-define! a-root name val +;;; nested-remove! a-root name +;;; +;;; +;;; (current-module) is a natural choice for a-root so for convenience there are +;;; also: +;;; +;;; local-ref name == nested-ref (current-module) name +;;; local-set! name val == nested-set! (current-module) name val +;;; local-define! name val == nested-define! (current-module) name val +;;; local-remove! name == nested-remove! (current-module) name +;;; + + +(define (nested-ref root names) + (let loop ((cur root) + (elts names)) + (cond + ((null? elts) cur) + ((not (module? cur)) #f) + (else (loop (module-ref cur (car elts) #f) (cdr elts)))))) + +(define (nested-set! root names val) + (let loop ((cur root) + (elts names)) + (if (null? (cdr elts)) + (module-set! cur (car elts) val) + (loop (module-ref cur (car elts)) (cdr elts))))) + +(define (nested-define! root names val) + (let loop ((cur root) + (elts names)) + (if (null? (cdr elts)) + (module-define! cur (car elts) val) + (loop (module-ref cur (car elts)) (cdr elts))))) + +(define (nested-remove! root names) + (let loop ((cur root) + (elts names)) + (if (null? (cdr elts)) + (module-remove! cur (car elts)) + (loop (module-ref cur (car elts)) (cdr elts))))) + +(define (local-ref names) (nested-ref (current-module) names)) +(define (local-set! names val) (nested-set! (current-module) names val)) +(define (local-define names val) (nested-define! (current-module) names val)) +(define (local-remove names) (nested-remove! (current-module) names)) +;;; boot-9.scm ends here diff --git a/tests/examplefiles/broken/fucked_up.rb b/tests/examplefiles/broken/fucked_up.rb new file mode 100644 index 0000000..b1d0ee3 --- /dev/null +++ b/tests/examplefiles/broken/fucked_up.rb @@ -0,0 +1,77 @@ +# vim:ft=ruby + +events = Hash.new { |h, k| h[k] = [] } +DATA.read.split(/\n\n\n\s*/).each do |event| + name = event[/^.*/].sub(/http:.*/, '') + event[/\n.*/m].scan(/^([A-Z]{2}\S*)\s*(\S*)\s*(\S*)(\s*\S*)/) do |kind, day, daytime, comment| + events[ [day, daytime] ] << [kind, name + comment] + end +end + +conflicts = 0 +events.to_a.sort_by do |(day, daytime),| + [%w(Mo Di Mi Do Fr).index(day) || 0, daytime] +end.each do |(day, daytime), names| + if names.size > 1 + conflicts += 1 + print '!!! ' + end + print "#{day} #{daytime}: " + names.each { |kind, name| puts " #{kind} #{name}" } + puts +end + +puts '%d conflicts' % conflicts +puts '%d SWS' % (events.inject(0) { |sum, ((day, daytime),)| sum + (daytime[/\d+$/].to_i - daytime[/^\d+/].to_i) }) + +string = % foo # strange. huh? +print "Escape here: \n" +print 'Dont escape here: \n' + +__END__ +Informatik und Informationsgesellschaft I: Digitale Medien (32 214) +Computer lassen ihre eigentliche Bestimmung durch Multimedia und Vernetzung erkennen: Es sind digitale Medien, die alle bisherigen Massen- und Kommunikationsmedien simulieren, kopieren oder ersetzen können. Die kurze Geschichte elektronischer Medien vom Telegramm bis zum Fernsehen wird so zur Vorgeschichte des Computers als Medium. Der Prozess der Mediatisierung der Rechnernetze soll in Technik, Theorie und Praxis untersucht werden. Das PR soll die Techniken der ortsverteilten und zeitversetzten Lehre an Hand praktischer Übungen vorführen und untersuchen. +VL Di 15-17 wöch. RUD 25, 3.101 J. Koubek +VL Do 15-17 wöch. RUD 25, 3.101 +UE/PR Do 17-19 wöch. RUD 25, 3.101 J.-M. Loebel + + +Methoden und Modelle des Systementwurfs (32 223) +Gute Methoden zum Entwurf und zur Verifikation von Systemen sind ein Schlüssel für gute Software. Dieses Seminar betrachtet moderne Entwurfsmethoden. + VL Di 09-11 wöch. RUD 26, 0’313 W. Reisig + VL Do 09-11 wöch. RUD 26, 0’313 + UE Di 11-13 wöch. RUD 26, 0’313 + PR Di 13-15 wöch. RUD 26, 0’313 D. Weinberg + + +Komplexitätstheorie (32 229) +In dieser Vorlesung untersuchen wir eine Reihe von wichtigen algorithmischen Problemstellungen aus verschiedenen Bereichen der Informatik. Unser besonderes Interesse gilt dabei der Abschätzung der Rechenressourcen, die zu ihrer Lösung aufzubringen sind. Die Vorlesung bildet eine wichtige Grundlage für weiterführende Veranstaltungen in den Bereichen Algorithmen, Kryptologie, Algorithmisches Lernen und Algorithmisches Beweisen. + VL Di 09-11 wöch. RUD 26, 1’303 J. Köbler + VL Do 09-11 wöch. RUD 26, 1’305 + UE Do 11-13 wöch. RUD 26, 1’305 + + +Zuverlässige Systeme (32 234) +Mit zunehmender Verbreitung der Computertechnologie in immer mehr Bereichen des menschlichen Lebens wird die Zuverlässigkeit solcher Systeme zu einer immer zentraleren Frage. +Der Halbkurs "Zuverlässige Systeme" konzentriert sich auf folgende Schwerpunkte: Zuverlässigkeit, Fehlertoleranz, Responsivität, Messungen, Anwendungen, Systemmodelle und Techniken, Ausfallverhalten, Fehlermodelle, Schedulingtechniken, Software/Hardware - responsives Systemdesign, Analyse und Synthese, Bewertung, Fallstudien in Forschung und Industrie. +Der Halbkurs kann mit dem Halbkurs "Eigenschaften mobiler und eingebetteter Systeme" zu einem Projektkurs kombiniert werden. Ein gemeinsames Projekt begleitet beide Halbkurse. +VL Di 09-11 wöch. RUD 26, 1’308 M. Malek +VL Do 09-11 wöch. RUD 26, 1’308 +PR n.V. + + +Stochastik für InformatikerInnen (32 239) +Grundlagen der Wahrscheinlichkeitsrechnung, Diskrete und stetige Wahrscheinlichkeitsmodelle in der Informatik, Grenzwertsätze, Simulationsverfahren, Zufallszahlen, Statistische Schätz- und Testverfahren, Markoffsche Ketten, Simulated Annealing, Probabilistische Analyse von Algorithmen. +VL Mo 09-11 wöch. RUD 25, 3.101 W. Kössler +VL Mi 09-11 wöch. RUD 25, 3.101 +UE Mo 11-13 wöch. RUD 25, 3.101 + UE Mi 11-13 wöch. RUD 25. 3.101 + + +Geschichte der Informatik – Ausgewählte Kapitel (32 243) +VL Mi 13-15 wöch. RUD 25, 3.113 W. Coy + + +Aktuelle Themen der Theoretischen Informatik (32 260) +In diesem Seminar sollen wichtige aktuelle Veröffentlichungen aus der theoretischen Informatik gemeinsam erarbeitet werden. Genaueres wird erst kurz vor dem Seminar entschieden. Bei Interesse wenden Sie sich bitte möglichst frühzeitig an den Veranstalter. + SE Fr 09-11 wöch. RUD 26, 1’307 M. Grohe  diff --git a/tests/examplefiles/ceval.c b/tests/examplefiles/ceval.c new file mode 100644 index 0000000..c673963 --- /dev/null +++ b/tests/examplefiles/ceval.c @@ -0,0 +1,2604 @@ + +/* Execute compiled code */ + +/* XXX TO DO: + XXX speed up searching for keywords by using a dictionary + XXX document it! + */ + +/* enable more aggressive intra-module optimizations, where available */ +#define PY_LOCAL_AGGRESSIVE + +#include "Python.h" + +#include "code.h" +#include "frameobject.h" +#include "eval.h" +#include "opcode.h" +#include "structmember.h" + +#include + +#ifndef WITH_TSC + +#define READ_TIMESTAMP(var) + +#else + +typedef unsigned long long uint64; + +#if defined(__ppc__) /* <- Don't know if this is the correct symbol; this + section should work for GCC on any PowerPC platform, + irrespective of OS. POWER? Who knows :-) */ + +#define READ_TIMESTAMP(var) ppc_getcounter(&var) + +static void +ppc_getcounter(uint64 *v) +{ + register unsigned long tbu, tb, tbu2; + + loop: + asm volatile ("mftbu %0" : "=r" (tbu) ); + asm volatile ("mftb %0" : "=r" (tb) ); + asm volatile ("mftbu %0" : "=r" (tbu2)); + if (__builtin_expect(tbu != tbu2, 0)) goto loop; + + /* The slightly peculiar way of writing the next lines is + compiled better by GCC than any other way I tried. */ + ((long*)(v))[0] = tbu; + ((long*)(v))[1] = tb; +} + +#else /* this is for linux/x86 (and probably any other GCC/x86 combo) */ + +#define READ_TIMESTAMP(val) \ + __asm__ __volatile__("rdtsc" : "=A" (val)) + +#endif + +void dump_tsc(int opcode, int ticked, uint64 inst0, uint64 inst1, + uint64 loop0, uint64 loop1, uint64 intr0, uint64 intr1) +{ + uint64 intr, inst, loop; + PyThreadState *tstate = PyThreadState_Get(); + if (!tstate->interp->tscdump) + return; + intr = intr1 - intr0; + inst = inst1 - inst0 - intr; + loop = loop1 - loop0 - intr; + fprintf(stderr, "opcode=%03d t=%d inst=%06lld loop=%06lld\n", + opcode, ticked, inst, loop); +} + +#endif + +/* Turn this on if your compiler chokes on the big switch: */ +/* #define CASE_TOO_BIG 1 */ + +#ifdef Py_DEBUG +/* For debugging the interpreter: */ +#define LLTRACE 1 /* Low-level trace feature */ +#define CHECKEXC 1 /* Double-check exception checking */ +#endif + +typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *); + +/* Forward declarations */ +#ifdef WITH_TSC +static PyObject * call_function(PyObject ***, int, uint64*, uint64*); +#else +static PyObject * call_function(PyObject ***, int); +#endif +static PyObject * fast_function(PyObject *, PyObject ***, int, int, int); +static PyObject * do_call(PyObject *, PyObject ***, int, int); +static PyObject * ext_do_call(PyObject *, PyObject ***, int, int, int); +static PyObject * update_keyword_args(PyObject *, int, PyObject ***,PyObject *); +static PyObject * update_star_args(int, int, PyObject *, PyObject ***); +static PyObject * load_args(PyObject ***, int); +#define CALL_FLAG_VAR 1 +#define CALL_FLAG_KW 2 + +#ifdef LLTRACE +static int lltrace; +static int prtrace(PyObject *, char *); +#endif +static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *, + int, PyObject *); +static void call_trace_protected(Py_tracefunc, PyObject *, + PyFrameObject *, int, PyObject *); +static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *); +static int maybe_call_line_trace(Py_tracefunc, PyObject *, + PyFrameObject *, int *, int *, int *); + +static PyObject * apply_slice(PyObject *, PyObject *, PyObject *); +static int assign_slice(PyObject *, PyObject *, + PyObject *, PyObject *); +static PyObject * cmp_outcome(int, PyObject *, PyObject *); +static PyObject * import_from(PyObject *, PyObject *); +static int import_all_from(PyObject *, PyObject *); +static PyObject * build_class(PyObject *, PyObject *, PyObject *); +static int exec_statement(PyFrameObject *, + PyObject *, PyObject *, PyObject *); +static void set_exc_info(PyThreadState *, PyObject *, PyObject *, PyObject *); +static void reset_exc_info(PyThreadState *); +static void format_exc_check_arg(PyObject *, char *, PyObject *); +static PyObject * string_concatenate(PyObject *, PyObject *, + PyFrameObject *, unsigned char *); + +#define NAME_ERROR_MSG \ + "name '%.200s' is not defined" +#define GLOBAL_NAME_ERROR_MSG \ + "global name '%.200s' is not defined" +#define UNBOUNDLOCAL_ERROR_MSG \ + "local variable '%.200s' referenced before assignment" +#define UNBOUNDFREE_ERROR_MSG \ + "free variable '%.200s' referenced before assignment" \ + " in enclosing scope" + +/* Dynamic execution profile */ +#ifdef DYNAMIC_EXECUTION_PROFILE +#ifdef DXPAIRS +static long dxpairs[257][256]; +#define dxp dxpairs[256] +#else +static long dxp[256]; +#endif +#endif + +/* Function call profile */ +#ifdef CALL_PROFILE +#define PCALL_NUM 11 +static int pcall[PCALL_NUM]; + +#define PCALL_ALL 0 +#define PCALL_FUNCTION 1 +#define PCALL_FAST_FUNCTION 2 +#define PCALL_FASTER_FUNCTION 3 +#define PCALL_METHOD 4 +#define PCALL_BOUND_METHOD 5 +#define PCALL_CFUNCTION 6 +#define PCALL_TYPE 7 +#define PCALL_GENERATOR 8 +#define PCALL_OTHER 9 +#define PCALL_POP 10 + +/* Notes about the statistics + + PCALL_FAST stats + + FAST_FUNCTION means no argument tuple needs to be created. + FASTER_FUNCTION means that the fast-path frame setup code is used. + + If there is a method call where the call can be optimized by changing + the argument tuple and calling the function directly, it gets recorded + twice. + + As a result, the relationship among the statistics appears to be + PCALL_ALL == PCALL_FUNCTION + PCALL_METHOD - PCALL_BOUND_METHOD + + PCALL_CFUNCTION + PCALL_TYPE + PCALL_GENERATOR + PCALL_OTHER + PCALL_FUNCTION > PCALL_FAST_FUNCTION > PCALL_FASTER_FUNCTION + PCALL_METHOD > PCALL_BOUND_METHOD +*/ + +#define PCALL(POS) pcall[POS]++ + +PyObject * +PyEval_GetCallStats(PyObject *self) +{ + return Py_BuildValue("iiiiiiiiii", + pcall[0], pcall[1], pcall[2], pcall[3], + pcall[4], pcall[5], pcall[6], pcall[7], + pcall[8], pcall[9]); +} +#else +#define PCALL(O) + +PyObject * +PyEval_GetCallStats(PyObject *self) +{ + Py_INCREF(Py_None); + return Py_None; +} +#endif + + +#ifdef WITH_THREAD + +#ifdef HAVE_ERRNO_H +#include +#endif +#include "pythread.h" + +static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */ +static long main_thread = 0; + +int +PyEval_ThreadsInitialized(void) +{ + return interpreter_lock != 0; +} + +void +PyEval_InitThreads(void) +{ + if (interpreter_lock) + return; + interpreter_lock = PyThread_allocate_lock(); + PyThread_acquire_lock(interpreter_lock, 1); + main_thread = PyThread_get_thread_ident(); +} + +void +PyEval_AcquireLock(void) +{ + PyThread_acquire_lock(interpreter_lock, 1); +} + +void +PyEval_ReleaseLock(void) +{ + PyThread_release_lock(interpreter_lock); +} + +void +PyEval_AcquireThread(PyThreadState *tstate) +{ + if (tstate == NULL) + Py_FatalError("PyEval_AcquireThread: NULL new thread state"); + /* Check someone has called PyEval_InitThreads() to create the lock */ + assert(interpreter_lock); + PyThread_acquire_lock(interpreter_lock, 1); + if (PyThreadState_Swap(tstate) != NULL) + Py_FatalError( + "PyEval_AcquireThread: non-NULL old thread state"); +} + +void +PyEval_ReleaseThread(PyThreadState *tstate) +{ + if (tstate == NULL) + Py_FatalError("PyEval_ReleaseThread: NULL thread state"); + if (PyThreadState_Swap(NULL) != tstate) + Py_FatalError("PyEval_ReleaseThread: wrong thread state"); + PyThread_release_lock(interpreter_lock); +} + +/* This function is called from PyOS_AfterFork to ensure that newly + created child processes don't hold locks referring to threads which + are not running in the child process. (This could also be done using + pthread_atfork mechanism, at least for the pthreads implementation.) */ + +void +PyEval_ReInitThreads(void) +{ + if (!interpreter_lock) + return; + /*XXX Can't use PyThread_free_lock here because it does too + much error-checking. Doing this cleanly would require + adding a new function to each thread_*.h. Instead, just + create a new lock and waste a little bit of memory */ + interpreter_lock = PyThread_allocate_lock(); + PyThread_acquire_lock(interpreter_lock, 1); + main_thread = PyThread_get_thread_ident(); +} +#endif + +/* Functions save_thread and restore_thread are always defined so + dynamically loaded modules needn't be compiled separately for use + with and without threads: */ + +PyThreadState * +PyEval_SaveThread(void) +{ + PyThreadState *tstate = PyThreadState_Swap(NULL); + if (tstate == NULL) + Py_FatalError("PyEval_SaveThread: NULL tstate"); +#ifdef WITH_THREAD + if (interpreter_lock) + PyThread_release_lock(interpreter_lock); +#endif + return tstate; +} + +void +PyEval_RestoreThread(PyThreadState *tstate) +{ + if (tstate == NULL) + Py_FatalError("PyEval_RestoreThread: NULL tstate"); +#ifdef WITH_THREAD + if (interpreter_lock) { + int err = errno; + PyThread_acquire_lock(interpreter_lock, 1); + errno = err; + } +#endif + PyThreadState_Swap(tstate); +} + + +/* Mechanism whereby asynchronously executing callbacks (e.g. UNIX + signal handlers or Mac I/O completion routines) can schedule calls + to a function to be called synchronously. + The synchronous function is called with one void* argument. + It should return 0 for success or -1 for failure -- failure should + be accompanied by an exception. + + If registry succeeds, the registry function returns 0; if it fails + (e.g. due to too many pending calls) it returns -1 (without setting + an exception condition). + + Note that because registry may occur from within signal handlers, + or other asynchronous events, calling malloc() is unsafe! + +#ifdef WITH_THREAD + Any thread can schedule pending calls, but only the main thread + will execute them. +#endif + + XXX WARNING! ASYNCHRONOUSLY EXECUTING CODE! + There are two possible race conditions: + (1) nested asynchronous registry calls; + (2) registry calls made while pending calls are being processed. + While (1) is very unlikely, (2) is a real possibility. + The current code is safe against (2), but not against (1). + The safety against (2) is derived from the fact that only one + thread (the main thread) ever takes things out of the queue. + + XXX Darn! With the advent of thread state, we should have an array + of pending calls per thread in the thread state! Later... +*/ + +#define NPENDINGCALLS 32 +static struct { + int (*func)(void *); + void *arg; +} pendingcalls[NPENDINGCALLS]; +static volatile int pendingfirst = 0; +static volatile int pendinglast = 0; +static volatile int things_to_do = 0; + +int +Py_AddPendingCall(int (*func)(void *), void *arg) +{ + static volatile int busy = 0; + int i, j; + /* XXX Begin critical section */ + /* XXX If you want this to be safe against nested + XXX asynchronous calls, you'll have to work harder! */ + if (busy) + return -1; + busy = 1; + i = pendinglast; + j = (i + 1) % NPENDINGCALLS; + if (j == pendingfirst) { + busy = 0; + return -1; /* Queue full */ + } + pendingcalls[i].func = func; + pendingcalls[i].arg = arg; + pendinglast = j; + + _Py_Ticker = 0; + things_to_do = 1; /* Signal main loop */ + busy = 0; + /* XXX End critical section */ + return 0; +} + +int +Py_MakePendingCalls(void) +{ + static int busy = 0; +#ifdef WITH_THREAD + if (main_thread && PyThread_get_thread_ident() != main_thread) + return 0; +#endif + if (busy) + return 0; + busy = 1; + things_to_do = 0; + for (;;) { + int i; + int (*func)(void *); + void *arg; + i = pendingfirst; + if (i == pendinglast) + break; /* Queue empty */ + func = pendingcalls[i].func; + arg = pendingcalls[i].arg; + pendingfirst = (i + 1) % NPENDINGCALLS; + if (func(arg) < 0) { + busy = 0; + things_to_do = 1; /* We're not done yet */ + return -1; + } + } + busy = 0; + return 0; +} + + +/* The interpreter's recursion limit */ + +#ifndef Py_DEFAULT_RECURSION_LIMIT +#define Py_DEFAULT_RECURSION_LIMIT 1000 +#endif +static int recursion_limit = Py_DEFAULT_RECURSION_LIMIT; +int _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT; + +int +Py_GetRecursionLimit(void) +{ + return recursion_limit; +} + +void +Py_SetRecursionLimit(int new_limit) +{ + recursion_limit = new_limit; + _Py_CheckRecursionLimit = recursion_limit; +} + +/* the macro Py_EnterRecursiveCall() only calls _Py_CheckRecursiveCall() + if the recursion_depth reaches _Py_CheckRecursionLimit. + If USE_STACKCHECK, the macro decrements _Py_CheckRecursionLimit + to guarantee that _Py_CheckRecursiveCall() is regularly called. + Without USE_STACKCHECK, there is no need for this. */ +int +_Py_CheckRecursiveCall(char *where) +{ + PyThreadState *tstate = PyThreadState_GET(); + +#ifdef USE_STACKCHECK + if (PyOS_CheckStack()) { + --tstate->recursion_depth; + PyErr_SetString(PyExc_MemoryError, "Stack overflow"); + return -1; + } +#endif + if (tstate->recursion_depth > recursion_limit) { + --tstate->recursion_depth; + PyErr_Format(PyExc_RuntimeError, + "maximum recursion depth exceeded%s", + where); + return -1; + } + _Py_CheckRecursionLimit = recursion_limit; + return 0; +} + +/* Status code for main loop (reason for stack unwind) */ +enum why_code { + WHY_NOT = 0x0001, /* No error */ + WHY_EXCEPTION = 0x0002, /* Exception occurred */ + WHY_RERAISE = 0x0004, /* Exception re-raised by 'finally' */ + WHY_RETURN = 0x0008, /* 'return' statement */ + WHY_BREAK = 0x0010, /* 'break' statement */ + WHY_CONTINUE = 0x0020, /* 'continue' statement */ + WHY_YIELD = 0x0040 /* 'yield' operator */ +}; + +static enum why_code do_raise(PyObject *, PyObject *, PyObject *); +static int unpack_iterable(PyObject *, int, PyObject **); + +/* for manipulating the thread switch and periodic "stuff" - used to be + per thread, now just a pair o' globals */ +int _Py_CheckInterval = 100; +volatile int _Py_Ticker = 100; + +PyObject * +PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals) +{ + /* XXX raise SystemError if globals is NULL */ + return PyEval_EvalCodeEx(co, + globals, locals, + (PyObject **)NULL, 0, + (PyObject **)NULL, 0, + (PyObject **)NULL, 0, + NULL); +} + + +/* Interpreter main loop */ + +PyObject * +PyEval_EvalFrame(PyFrameObject *f) { + /* This is for backward compatibility with extension modules that + used this API; core interpreter code should call PyEval_EvalFrameEx() */ + return PyEval_EvalFrameEx(f, 0); +} + +PyObject * +PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) +{ +#ifdef DXPAIRS + int lastopcode = 0; +#endif + register PyObject **stack_pointer; /* Next free slot in value stack */ + register unsigned char *next_instr; + register int opcode; /* Current opcode */ + register int oparg; /* Current opcode argument, if any */ + register enum why_code why; /* Reason for block stack unwind */ + register int err; /* Error status -- nonzero if error */ + register PyObject *x; /* Result object -- NULL if error */ + register PyObject *v; /* Temporary objects popped off stack */ + register PyObject *w; + register PyObject *u; + register PyObject *t; + register PyObject *stream = NULL; /* for PRINT opcodes */ + register PyObject **fastlocals, **freevars; + PyObject *retval = NULL; /* Return value */ + PyThreadState *tstate = PyThreadState_GET(); + PyCodeObject *co; + + /* when tracing we set things up so that + + not (instr_lb <= current_bytecode_offset < instr_ub) + + is true when the line being executed has changed. The + initial values are such as to make this false the first + time it is tested. */ + int instr_ub = -1, instr_lb = 0, instr_prev = -1; + + unsigned char *first_instr; + PyObject *names; + PyObject *consts; +#if defined(Py_DEBUG) || defined(LLTRACE) + /* Make it easier to find out where we are with a debugger */ + char *filename; +#endif + +/* Tuple access macros */ + +#ifndef Py_DEBUG +#define GETITEM(v, i) PyTuple_GET_ITEM((PyTupleObject *)(v), (i)) +#else +#define GETITEM(v, i) PyTuple_GetItem((v), (i)) +#endif + +#ifdef WITH_TSC +/* Use Pentium timestamp counter to mark certain events: + inst0 -- beginning of switch statement for opcode dispatch + inst1 -- end of switch statement (may be skipped) + loop0 -- the top of the mainloop + loop1 -- place where control returns again to top of mainloop + (may be skipped) + intr1 -- beginning of long interruption + intr2 -- end of long interruption + + Many opcodes call out to helper C functions. In some cases, the + time in those functions should be counted towards the time for the + opcode, but not in all cases. For example, a CALL_FUNCTION opcode + calls another Python function; there's no point in charge all the + bytecode executed by the called function to the caller. + + It's hard to make a useful judgement statically. In the presence + of operator overloading, it's impossible to tell if a call will + execute new Python code or not. + + It's a case-by-case judgement. I'll use intr1 for the following + cases: + + EXEC_STMT + IMPORT_STAR + IMPORT_FROM + CALL_FUNCTION (and friends) + + */ + uint64 inst0, inst1, loop0, loop1, intr0 = 0, intr1 = 0; + int ticked = 0; + + READ_TIMESTAMP(inst0); + READ_TIMESTAMP(inst1); + READ_TIMESTAMP(loop0); + READ_TIMESTAMP(loop1); + + /* shut up the compiler */ + opcode = 0; +#endif + +/* Code access macros */ + +#define INSTR_OFFSET() ((int)(next_instr - first_instr)) +#define NEXTOP() (*next_instr++) +#define NEXTARG() (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2]) +#define PEEKARG() ((next_instr[2]<<8) + next_instr[1]) +#define JUMPTO(x) (next_instr = first_instr + (x)) +#define JUMPBY(x) (next_instr += (x)) + +/* OpCode prediction macros + Some opcodes tend to come in pairs thus making it possible to predict + the second code when the first is run. For example, COMPARE_OP is often + followed by JUMP_IF_FALSE or JUMP_IF_TRUE. And, those opcodes are often + followed by a POP_TOP. + + Verifying the prediction costs a single high-speed test of register + variable against a constant. If the pairing was good, then the + processor has a high likelihood of making its own successful branch + prediction which results in a nearly zero overhead transition to the + next opcode. + + A successful prediction saves a trip through the eval-loop including + its two unpredictable branches, the HASARG test and the switch-case. + + If collecting opcode statistics, turn off prediction so that + statistics are accurately maintained (the predictions bypass + the opcode frequency counter updates). +*/ + +#ifdef DYNAMIC_EXECUTION_PROFILE +#define PREDICT(op) if (0) goto PRED_##op +#else +#define PREDICT(op) if (*next_instr == op) goto PRED_##op +#endif + +#define PREDICTED(op) PRED_##op: next_instr++ +#define PREDICTED_WITH_ARG(op) PRED_##op: oparg = PEEKARG(); next_instr += 3 + +/* Stack manipulation macros */ + +/* The stack can grow at most MAXINT deep, as co_nlocals and + co_stacksize are ints. */ +#define STACK_LEVEL() ((int)(stack_pointer - f->f_valuestack)) +#define EMPTY() (STACK_LEVEL() == 0) +#define TOP() (stack_pointer[-1]) +#define SECOND() (stack_pointer[-2]) +#define THIRD() (stack_pointer[-3]) +#define FOURTH() (stack_pointer[-4]) +#define SET_TOP(v) (stack_pointer[-1] = (v)) +#define SET_SECOND(v) (stack_pointer[-2] = (v)) +#define SET_THIRD(v) (stack_pointer[-3] = (v)) +#define SET_FOURTH(v) (stack_pointer[-4] = (v)) +#define BASIC_STACKADJ(n) (stack_pointer += n) +#define BASIC_PUSH(v) (*stack_pointer++ = (v)) +#define BASIC_POP() (*--stack_pointer) + +#ifdef LLTRACE +#define PUSH(v) { (void)(BASIC_PUSH(v), \ + lltrace && prtrace(TOP(), "push")); \ + assert(STACK_LEVEL() <= co->co_stacksize); } +#define POP() ((void)(lltrace && prtrace(TOP(), "pop")), BASIC_POP()) +#define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \ + lltrace && prtrace(TOP(), "stackadj")); \ + assert(STACK_LEVEL() <= co->co_stacksize); } +#define EXT_POP(STACK_POINTER) (lltrace && prtrace(*(STACK_POINTER), "ext_pop"), *--(STACK_POINTER)) +#else +#define PUSH(v) BASIC_PUSH(v) +#define POP() BASIC_POP() +#define STACKADJ(n) BASIC_STACKADJ(n) +#define EXT_POP(STACK_POINTER) (*--(STACK_POINTER)) +#endif + +/* Local variable macros */ + +#define GETLOCAL(i) (fastlocals[i]) + +/* The SETLOCAL() macro must not DECREF the local variable in-place and + then store the new value; it must copy the old value to a temporary + value, then store the new value, and then DECREF the temporary value. + This is because it is possible that during the DECREF the frame is + accessed by other code (e.g. a __del__ method or gc.collect()) and the + variable would be pointing to already-freed memory. */ +#define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); \ + GETLOCAL(i) = value; \ + Py_XDECREF(tmp); } while (0) + +/* Start of code */ + + if (f == NULL) + return NULL; + + /* push frame */ + if (Py_EnterRecursiveCall("")) + return NULL; + + tstate->frame = f; + + if (tstate->use_tracing) { + if (tstate->c_tracefunc != NULL) { + /* tstate->c_tracefunc, if defined, is a + function that will be called on *every* entry + to a code block. Its return value, if not + None, is a function that will be called at + the start of each executed line of code. + (Actually, the function must return itself + in order to continue tracing.) The trace + functions are called with three arguments: + a pointer to the current frame, a string + indicating why the function is called, and + an argument which depends on the situation. + The global trace function is also called + whenever an exception is detected. */ + if (call_trace(tstate->c_tracefunc, tstate->c_traceobj, + f, PyTrace_CALL, Py_None)) { + /* Trace function raised an error */ + goto exit_eval_frame; + } + } + if (tstate->c_profilefunc != NULL) { + /* Similar for c_profilefunc, except it needn't + return itself and isn't called for "line" events */ + if (call_trace(tstate->c_profilefunc, + tstate->c_profileobj, + f, PyTrace_CALL, Py_None)) { + /* Profile function raised an error */ + goto exit_eval_frame; + } + } + } + + co = f->f_code; + names = co->co_names; + consts = co->co_consts; + fastlocals = f->f_localsplus; + freevars = f->f_localsplus + co->co_nlocals; + first_instr = (unsigned char*) PyString_AS_STRING(co->co_code); + /* An explanation is in order for the next line. + + f->f_lasti now refers to the index of the last instruction + executed. You might think this was obvious from the name, but + this wasn't always true before 2.3! PyFrame_New now sets + f->f_lasti to -1 (i.e. the index *before* the first instruction) + and YIELD_VALUE doesn't fiddle with f_lasti any more. So this + does work. Promise. */ + next_instr = first_instr + f->f_lasti + 1; + stack_pointer = f->f_stacktop; + assert(stack_pointer != NULL); + f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ + +#ifdef LLTRACE + lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL; +#endif +#if defined(Py_DEBUG) || defined(LLTRACE) + filename = PyString_AsString(co->co_filename); +#endif + + why = WHY_NOT; + err = 0; + x = Py_None; /* Not a reference, just anything non-NULL */ + w = NULL; + + if (throwflag) { /* support for generator.throw() */ + why = WHY_EXCEPTION; + goto on_error; + } + + for (;;) { +#ifdef WITH_TSC + if (inst1 == 0) { + /* Almost surely, the opcode executed a break + or a continue, preventing inst1 from being set + on the way out of the loop. + */ + READ_TIMESTAMP(inst1); + loop1 = inst1; + } + dump_tsc(opcode, ticked, inst0, inst1, loop0, loop1, + intr0, intr1); + ticked = 0; + inst1 = 0; + intr0 = 0; + intr1 = 0; + READ_TIMESTAMP(loop0); +#endif + assert(stack_pointer >= f->f_valuestack); /* else underflow */ + assert(STACK_LEVEL() <= co->co_stacksize); /* else overflow */ + + /* Do periodic things. Doing this every time through + the loop would add too much overhead, so we do it + only every Nth instruction. We also do it if + ``things_to_do'' is set, i.e. when an asynchronous + event needs attention (e.g. a signal handler or + async I/O handler); see Py_AddPendingCall() and + Py_MakePendingCalls() above. */ + + if (--_Py_Ticker < 0) { + if (*next_instr == SETUP_FINALLY) { + /* Make the last opcode before + a try: finally: block uninterruptable. */ + goto fast_next_opcode; + } + _Py_Ticker = _Py_CheckInterval; + tstate->tick_counter++; +#ifdef WITH_TSC + ticked = 1; +#endif + if (things_to_do) { + if (Py_MakePendingCalls() < 0) { + why = WHY_EXCEPTION; + goto on_error; + } + if (things_to_do) + /* MakePendingCalls() didn't succeed. + Force early re-execution of this + "periodic" code, possibly after + a thread switch */ + _Py_Ticker = 0; + } +#ifdef WITH_THREAD + if (interpreter_lock) { + /* Give another thread a chance */ + + if (PyThreadState_Swap(NULL) != tstate) + Py_FatalError("ceval: tstate mix-up"); + PyThread_release_lock(interpreter_lock); + + /* Other threads may run now */ + + PyThread_acquire_lock(interpreter_lock, 1); + if (PyThreadState_Swap(tstate) != NULL) + Py_FatalError("ceval: orphan tstate"); + + /* Check for thread interrupts */ + + if (tstate->async_exc != NULL) { + x = tstate->async_exc; + tstate->async_exc = NULL; + PyErr_SetNone(x); + Py_DECREF(x); + why = WHY_EXCEPTION; + goto on_error; + } + } +#endif + } + + fast_next_opcode: + f->f_lasti = INSTR_OFFSET(); + + /* line-by-line tracing support */ + + if (tstate->c_tracefunc != NULL && !tstate->tracing) { + /* see maybe_call_line_trace + for expository comments */ + f->f_stacktop = stack_pointer; + + err = maybe_call_line_trace(tstate->c_tracefunc, + tstate->c_traceobj, + f, &instr_lb, &instr_ub, + &instr_prev); + /* Reload possibly changed frame fields */ + JUMPTO(f->f_lasti); + if (f->f_stacktop != NULL) { + stack_pointer = f->f_stacktop; + f->f_stacktop = NULL; + } + if (err) { + /* trace function raised an exception */ + goto on_error; + } + } + + /* Extract opcode and argument */ + + opcode = NEXTOP(); + oparg = 0; /* allows oparg to be stored in a register because + it doesn't have to be remembered across a full loop */ + if (HAS_ARG(opcode)) + oparg = NEXTARG(); + dispatch_opcode: +#ifdef DYNAMIC_EXECUTION_PROFILE +#ifdef DXPAIRS + dxpairs[lastopcode][opcode]++; + lastopcode = opcode; +#endif + dxp[opcode]++; +#endif + +#ifdef LLTRACE + /* Instruction tracing */ + + if (lltrace) { + if (HAS_ARG(opcode)) { + printf("%d: %d, %d\n", + f->f_lasti, opcode, oparg); + } + else { + printf("%d: %d\n", + f->f_lasti, opcode); + } + } +#endif + + /* Main switch on opcode */ + READ_TIMESTAMP(inst0); + + switch (opcode) { + + /* BEWARE! + It is essential that any operation that fails sets either + x to NULL, err to nonzero, or why to anything but WHY_NOT, + and that no operation that succeeds does this! */ + + /* case STOP_CODE: this is an error! */ + + case NOP: + goto fast_next_opcode; + + case LOAD_FAST: + x = GETLOCAL(oparg); + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + goto fast_next_opcode; + } + format_exc_check_arg(PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(co->co_varnames, oparg)); + break; + + case LOAD_CONST: + x = GETITEM(consts, oparg); + Py_INCREF(x); + PUSH(x); + goto fast_next_opcode; + + PREDICTED_WITH_ARG(STORE_FAST); + case STORE_FAST: + v = POP(); + SETLOCAL(oparg, v); + goto fast_next_opcode; + + PREDICTED(POP_TOP); + case POP_TOP: + v = POP(); + Py_DECREF(v); + goto fast_next_opcode; + + case ROT_TWO: + v = TOP(); + w = SECOND(); + SET_TOP(w); + SET_SECOND(v); + goto fast_next_opcode; + + case ROT_THREE: + v = TOP(); + w = SECOND(); + x = THIRD(); + SET_TOP(w); + SET_SECOND(x); + SET_THIRD(v); + goto fast_next_opcode; + + case ROT_FOUR: + u = TOP(); + v = SECOND(); + w = THIRD(); + x = FOURTH(); + SET_TOP(v); + SET_SECOND(w); + SET_THIRD(x); + SET_FOURTH(u); + goto fast_next_opcode; + + case DUP_TOP: + v = TOP(); + Py_INCREF(v); + PUSH(v); + goto fast_next_opcode; + + case DUP_TOPX: + if (oparg == 2) { + x = TOP(); + Py_INCREF(x); + w = SECOND(); + Py_INCREF(w); + STACKADJ(2); + SET_TOP(x); + SET_SECOND(w); + goto fast_next_opcode; + } else if (oparg == 3) { + x = TOP(); + Py_INCREF(x); + w = SECOND(); + Py_INCREF(w); + v = THIRD(); + Py_INCREF(v); + STACKADJ(3); + SET_TOP(x); + SET_SECOND(w); + SET_THIRD(v); + goto fast_next_opcode; + } + Py_FatalError("invalid argument to DUP_TOPX" + " (bytecode corruption?)"); + break; + + case UNARY_POSITIVE: + v = TOP(); + x = PyNumber_Positive(v); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case UNARY_NEGATIVE: + v = TOP(); + x = PyNumber_Negative(v); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case UNARY_NOT: + v = TOP(); + err = PyObject_IsTrue(v); + Py_DECREF(v); + if (err == 0) { + Py_INCREF(Py_True); + SET_TOP(Py_True); + continue; + } + else if (err > 0) { + Py_INCREF(Py_False); + SET_TOP(Py_False); + err = 0; + continue; + } + STACKADJ(-1); + break; + + case UNARY_CONVERT: + v = TOP(); + x = PyObject_Repr(v); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case UNARY_INVERT: + v = TOP(); + x = PyNumber_Invert(v); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_POWER: + w = POP(); + v = TOP(); + x = PyNumber_Power(v, w, Py_None); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_MULTIPLY: + w = POP(); + v = TOP(); + x = PyNumber_Multiply(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_DIVIDE: + if (!_Py_QnewFlag) { + w = POP(); + v = TOP(); + x = PyNumber_Divide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + } + /* -Qnew is in effect: fall through to + BINARY_TRUE_DIVIDE */ + case BINARY_TRUE_DIVIDE: + w = POP(); + v = TOP(); + x = PyNumber_TrueDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_FLOOR_DIVIDE: + w = POP(); + v = TOP(); + x = PyNumber_FloorDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_MODULO: + w = POP(); + v = TOP(); + x = PyNumber_Remainder(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_ADD: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: int + int */ + register long a, b, i; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + i = a + b; + if ((i^a) < 0 && (i^b) < 0) + goto slow_add; + x = PyInt_FromLong(i); + } + else if (PyString_CheckExact(v) && + PyString_CheckExact(w)) { + x = string_concatenate(v, w, f, next_instr); + /* string_concatenate consumed the ref to v */ + goto skip_decref_vx; + } + else { + slow_add: + x = PyNumber_Add(v, w); + } + Py_DECREF(v); + skip_decref_vx: + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_SUBTRACT: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: int - int */ + register long a, b, i; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + i = a - b; + if ((i^a) < 0 && (i^~b) < 0) + goto slow_sub; + x = PyInt_FromLong(i); + } + else { + slow_sub: + x = PyNumber_Subtract(v, w); + } + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_SUBSCR: + w = POP(); + v = TOP(); + if (PyList_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: list[int] */ + Py_ssize_t i = PyInt_AsSsize_t(w); + if (i < 0) + i += PyList_GET_SIZE(v); + if (i >= 0 && i < PyList_GET_SIZE(v)) { + x = PyList_GET_ITEM(v, i); + Py_INCREF(x); + } + else + goto slow_get; + } + else + slow_get: + x = PyObject_GetItem(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_LSHIFT: + w = POP(); + v = TOP(); + x = PyNumber_Lshift(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_RSHIFT: + w = POP(); + v = TOP(); + x = PyNumber_Rshift(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_AND: + w = POP(); + v = TOP(); + x = PyNumber_And(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_XOR: + w = POP(); + v = TOP(); + x = PyNumber_Xor(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_OR: + w = POP(); + v = TOP(); + x = PyNumber_Or(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case LIST_APPEND: + w = POP(); + v = POP(); + err = PyList_Append(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (err == 0) { + PREDICT(JUMP_ABSOLUTE); + continue; + } + break; + + case INPLACE_POWER: + w = POP(); + v = TOP(); + x = PyNumber_InPlacePower(v, w, Py_None); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_MULTIPLY: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceMultiply(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_DIVIDE: + if (!_Py_QnewFlag) { + w = POP(); + v = TOP(); + x = PyNumber_InPlaceDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + } + /* -Qnew is in effect: fall through to + INPLACE_TRUE_DIVIDE */ + case INPLACE_TRUE_DIVIDE: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceTrueDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_FLOOR_DIVIDE: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceFloorDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_MODULO: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceRemainder(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_ADD: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: int + int */ + register long a, b, i; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + i = a + b; + if ((i^a) < 0 && (i^b) < 0) + goto slow_iadd; + x = PyInt_FromLong(i); + } + else if (PyString_CheckExact(v) && + PyString_CheckExact(w)) { + x = string_concatenate(v, w, f, next_instr); + /* string_concatenate consumed the ref to v */ + goto skip_decref_v; + } + else { + slow_iadd: + x = PyNumber_InPlaceAdd(v, w); + } + Py_DECREF(v); + skip_decref_v: + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_SUBTRACT: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: int - int */ + register long a, b, i; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + i = a - b; + if ((i^a) < 0 && (i^~b) < 0) + goto slow_isub; + x = PyInt_FromLong(i); + } + else { + slow_isub: + x = PyNumber_InPlaceSubtract(v, w); + } + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_LSHIFT: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceLshift(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_RSHIFT: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceRshift(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_AND: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceAnd(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_XOR: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceXor(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_OR: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceOr(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case SLICE+0: + case SLICE+1: + case SLICE+2: + case SLICE+3: + if ((opcode-SLICE) & 2) + w = POP(); + else + w = NULL; + if ((opcode-SLICE) & 1) + v = POP(); + else + v = NULL; + u = TOP(); + x = apply_slice(u, v, w); + Py_DECREF(u); + Py_XDECREF(v); + Py_XDECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case STORE_SLICE+0: + case STORE_SLICE+1: + case STORE_SLICE+2: + case STORE_SLICE+3: + if ((opcode-STORE_SLICE) & 2) + w = POP(); + else + w = NULL; + if ((opcode-STORE_SLICE) & 1) + v = POP(); + else + v = NULL; + u = POP(); + t = POP(); + err = assign_slice(u, v, w, t); /* u[v:w] = t */ + Py_DECREF(t); + Py_DECREF(u); + Py_XDECREF(v); + Py_XDECREF(w); + if (err == 0) continue; + break; + + case DELETE_SLICE+0: + case DELETE_SLICE+1: + case DELETE_SLICE+2: + case DELETE_SLICE+3: + if ((opcode-DELETE_SLICE) & 2) + w = POP(); + else + w = NULL; + if ((opcode-DELETE_SLICE) & 1) + v = POP(); + else + v = NULL; + u = POP(); + err = assign_slice(u, v, w, (PyObject *)NULL); + /* del u[v:w] */ + Py_DECREF(u); + Py_XDECREF(v); + Py_XDECREF(w); + if (err == 0) continue; + break; + + case STORE_SUBSCR: + w = TOP(); + v = SECOND(); + u = THIRD(); + STACKADJ(-3); + /* v[w] = u */ + err = PyObject_SetItem(v, w, u); + Py_DECREF(u); + Py_DECREF(v); + Py_DECREF(w); + if (err == 0) continue; + break; + + case DELETE_SUBSCR: + w = TOP(); + v = SECOND(); + STACKADJ(-2); + /* del v[w] */ + err = PyObject_DelItem(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (err == 0) continue; + break; + + case PRINT_EXPR: + v = POP(); + w = PySys_GetObject("displayhook"); + if (w == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "lost sys.displayhook"); + err = -1; + x = NULL; + } + if (err == 0) { + x = PyTuple_Pack(1, v); + if (x == NULL) + err = -1; + } + if (err == 0) { + w = PyEval_CallObject(w, x); + Py_XDECREF(w); + if (w == NULL) + err = -1; + } + Py_DECREF(v); + Py_XDECREF(x); + break; + + case PRINT_ITEM_TO: + w = stream = POP(); + /* fall through to PRINT_ITEM */ + + case PRINT_ITEM: + v = POP(); + if (stream == NULL || stream == Py_None) { + w = PySys_GetObject("stdout"); + if (w == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "lost sys.stdout"); + err = -1; + } + } + /* PyFile_SoftSpace() can exececute arbitrary code + if sys.stdout is an instance with a __getattr__. + If __getattr__ raises an exception, w will + be freed, so we need to prevent that temporarily. */ + Py_XINCREF(w); + if (w != NULL && PyFile_SoftSpace(w, 0)) + err = PyFile_WriteString(" ", w); + if (err == 0) + err = PyFile_WriteObject(v, w, Py_PRINT_RAW); + if (err == 0) { + /* XXX move into writeobject() ? */ + if (PyString_Check(v)) { + char *s = PyString_AS_STRING(v); + Py_ssize_t len = PyString_GET_SIZE(v); + if (len == 0 || + !isspace(Py_CHARMASK(s[len-1])) || + s[len-1] == ' ') + PyFile_SoftSpace(w, 1); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(v)) { + Py_UNICODE *s = PyUnicode_AS_UNICODE(v); + Py_ssize_t len = PyUnicode_GET_SIZE(v); + if (len == 0 || + !Py_UNICODE_ISSPACE(s[len-1]) || + s[len-1] == ' ') + PyFile_SoftSpace(w, 1); + } +#endif + else + PyFile_SoftSpace(w, 1); + } + Py_XDECREF(w); + Py_DECREF(v); + Py_XDECREF(stream); + stream = NULL; + if (err == 0) + continue; + break; + + case PRINT_NEWLINE_TO: + w = stream = POP(); + /* fall through to PRINT_NEWLINE */ + + case PRINT_NEWLINE: + if (stream == NULL || stream == Py_None) { + w = PySys_GetObject("stdout"); + if (w == NULL) + PyErr_SetString(PyExc_RuntimeError, + "lost sys.stdout"); + } + if (w != NULL) { + err = PyFile_WriteString("\n", w); + if (err == 0) + PyFile_SoftSpace(w, 0); + } + Py_XDECREF(stream); + stream = NULL; + break; + + +#ifdef CASE_TOO_BIG + default: switch (opcode) { +#endif + case RAISE_VARARGS: + u = v = w = NULL; + switch (oparg) { + case 3: + u = POP(); /* traceback */ + /* Fallthrough */ + case 2: + v = POP(); /* value */ + /* Fallthrough */ + case 1: + w = POP(); /* exc */ + case 0: /* Fallthrough */ + why = do_raise(w, v, u); + break; + default: + PyErr_SetString(PyExc_SystemError, + "bad RAISE_VARARGS oparg"); + why = WHY_EXCEPTION; + break; + } + break; + + case LOAD_LOCALS: + if ((x = f->f_locals) != NULL) { + Py_INCREF(x); + PUSH(x); + continue; + } + PyErr_SetString(PyExc_SystemError, "no locals"); + break; + + case RETURN_VALUE: + retval = POP(); + why = WHY_RETURN; + goto fast_block_end; + + case YIELD_VALUE: + retval = POP(); + f->f_stacktop = stack_pointer; + why = WHY_YIELD; + goto fast_yield; + + case EXEC_STMT: + w = TOP(); + v = SECOND(); + u = THIRD(); + STACKADJ(-3); + READ_TIMESTAMP(intr0); + err = exec_statement(f, u, v, w); + READ_TIMESTAMP(intr1); + Py_DECREF(u); + Py_DECREF(v); + Py_DECREF(w); + break; + + case POP_BLOCK: + { + PyTryBlock *b = PyFrame_BlockPop(f); + while (STACK_LEVEL() > b->b_level) { + v = POP(); + Py_DECREF(v); + } + } + continue; + + case END_FINALLY: + v = POP(); + if (PyInt_Check(v)) { + why = (enum why_code) PyInt_AS_LONG(v); + assert(why != WHY_YIELD); + if (why == WHY_RETURN || + why == WHY_CONTINUE) + retval = POP(); + } + else if (PyExceptionClass_Check(v) || PyString_Check(v)) { + w = POP(); + u = POP(); + PyErr_Restore(v, w, u); + why = WHY_RERAISE; + break; + } + else if (v != Py_None) { + PyErr_SetString(PyExc_SystemError, + "'finally' pops bad exception"); + why = WHY_EXCEPTION; + } + Py_DECREF(v); + break; + + case BUILD_CLASS: + u = TOP(); + v = SECOND(); + w = THIRD(); + STACKADJ(-2); + x = build_class(u, v, w); + SET_TOP(x); + Py_DECREF(u); + Py_DECREF(v); + Py_DECREF(w); + break; + + case STORE_NAME: + w = GETITEM(names, oparg); + v = POP(); + if ((x = f->f_locals) != NULL) { + if (PyDict_CheckExact(x)) + err = PyDict_SetItem(x, w, v); + else + err = PyObject_SetItem(x, w, v); + Py_DECREF(v); + if (err == 0) continue; + break; + } + PyErr_Format(PyExc_SystemError, + "no locals found when storing %s", + PyObject_REPR(w)); + break; + + case DELETE_NAME: + w = GETITEM(names, oparg); + if ((x = f->f_locals) != NULL) { + if ((err = PyObject_DelItem(x, w)) != 0) + format_exc_check_arg(PyExc_NameError, + NAME_ERROR_MSG ,w); + break; + } + PyErr_Format(PyExc_SystemError, + "no locals when deleting %s", + PyObject_REPR(w)); + break; + + PREDICTED_WITH_ARG(UNPACK_SEQUENCE); + case UNPACK_SEQUENCE: + v = POP(); + if (PyTuple_CheckExact(v) && PyTuple_GET_SIZE(v) == oparg) { + PyObject **items = ((PyTupleObject *)v)->ob_item; + while (oparg--) { + w = items[oparg]; + Py_INCREF(w); + PUSH(w); + } + Py_DECREF(v); + continue; + } else if (PyList_CheckExact(v) && PyList_GET_SIZE(v) == oparg) { + PyObject **items = ((PyListObject *)v)->ob_item; + while (oparg--) { + w = items[oparg]; + Py_INCREF(w); + PUSH(w); + } + } else if (unpack_iterable(v, oparg, + stack_pointer + oparg)) + stack_pointer += oparg; + else { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_SetString(PyExc_TypeError, + "unpack non-sequence"); + why = WHY_EXCEPTION; + } + Py_DECREF(v); + break; + + case STORE_ATTR: + w = GETITEM(names, oparg); + v = TOP(); + u = SECOND(); + STACKADJ(-2); + err = PyObject_SetAttr(v, w, u); /* v.w = u */ + Py_DECREF(v); + Py_DECREF(u); + if (err == 0) continue; + break; + + case DELETE_ATTR: + w = GETITEM(names, oparg); + v = POP(); + err = PyObject_SetAttr(v, w, (PyObject *)NULL); + /* del v.w */ + Py_DECREF(v); + break; + + case STORE_GLOBAL: + w = GETITEM(names, oparg); + v = POP(); + err = PyDict_SetItem(f->f_globals, w, v); + Py_DECREF(v); + if (err == 0) continue; + break; + + case DELETE_GLOBAL: + w = GETITEM(names, oparg); + if ((err = PyDict_DelItem(f->f_globals, w)) != 0) + format_exc_check_arg( + PyExc_NameError, GLOBAL_NAME_ERROR_MSG, w); + break; + + case LOAD_NAME: + w = GETITEM(names, oparg); + if ((v = f->f_locals) == NULL) { + PyErr_Format(PyExc_SystemError, + "no locals when loading %s", + PyObject_REPR(w)); + break; + } + if (PyDict_CheckExact(v)) { + x = PyDict_GetItem(v, w); + Py_XINCREF(x); + } + else { + x = PyObject_GetItem(v, w); + if (x == NULL && PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) + break; + PyErr_Clear(); + } + } + if (x == NULL) { + x = PyDict_GetItem(f->f_globals, w); + if (x == NULL) { + x = PyDict_GetItem(f->f_builtins, w); + if (x == NULL) { + format_exc_check_arg( + PyExc_NameError, + NAME_ERROR_MSG ,w); + break; + } + } + Py_INCREF(x); + } + PUSH(x); + continue; + + case LOAD_GLOBAL: + w = GETITEM(names, oparg); + if (PyString_CheckExact(w)) { + /* Inline the PyDict_GetItem() calls. + WARNING: this is an extreme speed hack. + Do not try this at home. */ + long hash = ((PyStringObject *)w)->ob_shash; + if (hash != -1) { + PyDictObject *d; + PyDictEntry *e; + d = (PyDictObject *)(f->f_globals); + e = d->ma_lookup(d, w, hash); + if (e == NULL) { + x = NULL; + break; + } + x = e->me_value; + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + continue; + } + d = (PyDictObject *)(f->f_builtins); + e = d->ma_lookup(d, w, hash); + if (e == NULL) { + x = NULL; + break; + } + x = e->me_value; + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + continue; + } + goto load_global_error; + } + } + /* This is the un-inlined version of the code above */ + x = PyDict_GetItem(f->f_globals, w); + if (x == NULL) { + x = PyDict_GetItem(f->f_builtins, w); + if (x == NULL) { + load_global_error: + format_exc_check_arg( + PyExc_NameError, + GLOBAL_NAME_ERROR_MSG, w); + break; + } + } + Py_INCREF(x); + PUSH(x); + continue; + + case DELETE_FAST: + x = GETLOCAL(oparg); + if (x != NULL) { + SETLOCAL(oparg, NULL); + continue; + } + format_exc_check_arg( + PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(co->co_varnames, oparg) + ); + break; + + case LOAD_CLOSURE: + x = freevars[oparg]; + Py_INCREF(x); + PUSH(x); + if (x != NULL) continue; + break; + + case LOAD_DEREF: + x = freevars[oparg]; + w = PyCell_Get(x); + if (w != NULL) { + PUSH(w); + continue; + } + err = -1; + /* Don't stomp existing exception */ + if (PyErr_Occurred()) + break; + if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) { + v = PyTuple_GET_ITEM(co->co_cellvars, + oparg); + format_exc_check_arg( + PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + v); + } else { + v = PyTuple_GET_ITEM( + co->co_freevars, + oparg - PyTuple_GET_SIZE(co->co_cellvars)); + format_exc_check_arg( + PyExc_NameError, + UNBOUNDFREE_ERROR_MSG, + v); + } + break; + + case STORE_DEREF: + w = POP(); + x = freevars[oparg]; + PyCell_Set(x, w); + Py_DECREF(w); + continue; + + case BUILD_TUPLE: + x = PyTuple_New(oparg); + if (x != NULL) { + for (; --oparg >= 0;) { + w = POP(); + PyTuple_SET_ITEM(x, oparg, w); + } + PUSH(x); + continue; + } + break; + + case BUILD_LIST: + x = PyList_New(oparg); + if (x != NULL) { + for (; --oparg >= 0;) { + w = POP(); + PyList_SET_ITEM(x, oparg, w); + } + PUSH(x); + continue; + } + break; + + case BUILD_MAP: + x = PyDict_New(); + PUSH(x); + if (x != NULL) continue; + break; + + case LOAD_ATTR: + w = GETITEM(names, oparg); + v = TOP(); + x = PyObject_GetAttr(v, w); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case COMPARE_OP: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) { + /* INLINE: cmp(int, int) */ + register long a, b; + register int res; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + switch (oparg) { + case PyCmp_LT: res = a < b; break; + case PyCmp_LE: res = a <= b; break; + case PyCmp_EQ: res = a == b; break; + case PyCmp_NE: res = a != b; break; + case PyCmp_GT: res = a > b; break; + case PyCmp_GE: res = a >= b; break; + case PyCmp_IS: res = v == w; break; + case PyCmp_IS_NOT: res = v != w; break; + default: goto slow_compare; + } + x = res ? Py_True : Py_False; + Py_INCREF(x); + } + else { + slow_compare: + x = cmp_outcome(oparg, v, w); + } + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x == NULL) break; + PREDICT(JUMP_IF_FALSE); + PREDICT(JUMP_IF_TRUE); + continue; + + case IMPORT_NAME: + w = GETITEM(names, oparg); + x = PyDict_GetItemString(f->f_builtins, "__import__"); + if (x == NULL) { + PyErr_SetString(PyExc_ImportError, + "__import__ not found"); + break; + } + v = POP(); + u = TOP(); + if (PyInt_AsLong(u) != -1 || PyErr_Occurred()) + w = PyTuple_Pack(5, + w, + f->f_globals, + f->f_locals == NULL ? + Py_None : f->f_locals, + v, + u); + else + w = PyTuple_Pack(4, + w, + f->f_globals, + f->f_locals == NULL ? + Py_None : f->f_locals, + v); + Py_DECREF(v); + Py_DECREF(u); + if (w == NULL) { + u = POP(); + x = NULL; + break; + } + READ_TIMESTAMP(intr0); + x = PyEval_CallObject(x, w); + READ_TIMESTAMP(intr1); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case IMPORT_STAR: + v = POP(); + PyFrame_FastToLocals(f); + if ((x = f->f_locals) == NULL) { + PyErr_SetString(PyExc_SystemError, + "no locals found during 'import *'"); + break; + } + READ_TIMESTAMP(intr0); + err = import_all_from(x, v); + READ_TIMESTAMP(intr1); + PyFrame_LocalsToFast(f, 0); + Py_DECREF(v); + if (err == 0) continue; + break; + + case IMPORT_FROM: + w = GETITEM(names, oparg); + v = TOP(); + READ_TIMESTAMP(intr0); + x = import_from(v, w); + READ_TIMESTAMP(intr1); + PUSH(x); + if (x != NULL) continue; + break; + + case JUMP_FORWARD: + JUMPBY(oparg); + goto fast_next_opcode; + + PREDICTED_WITH_ARG(JUMP_IF_FALSE); + case JUMP_IF_FALSE: + w = TOP(); + if (w == Py_True) { + PREDICT(POP_TOP); + goto fast_next_opcode; + } + if (w == Py_False) { + JUMPBY(oparg); + goto fast_next_opcode; + } + err = PyObject_IsTrue(w); + if (err > 0) + err = 0; + else if (err == 0) + JUMPBY(oparg); + else + break; + continue; + + PREDICTED_WITH_ARG(JUMP_IF_TRUE); + case JUMP_IF_TRUE: + w = TOP(); + if (w == Py_False) { + PREDICT(POP_TOP); + goto fast_next_opcode; + } + if (w == Py_True) { + JUMPBY(oparg); + goto fast_next_opcode; + } + err = PyObject_IsTrue(w); + if (err > 0) { + err = 0; + JUMPBY(oparg); + } + else if (err == 0) + ; + else + break; + continue; + + PREDICTED_WITH_ARG(JUMP_ABSOLUTE); + case JUMP_ABSOLUTE: + JUMPTO(oparg); + continue; + + case GET_ITER: + /* before: [obj]; after [getiter(obj)] */ + v = TOP(); + x = PyObject_GetIter(v); + Py_DECREF(v); + if (x != NULL) { + SET_TOP(x); + PREDICT(FOR_ITER); + continue; + } + STACKADJ(-1); + break; + + PREDICTED_WITH_ARG(FOR_ITER); + case FOR_ITER: + /* before: [iter]; after: [iter, iter()] *or* [] */ + v = TOP(); + x = (*v->ob_type->tp_iternext)(v); + if (x != NULL) { + PUSH(x); + PREDICT(STORE_FAST); + PREDICT(UNPACK_SEQUENCE); + continue; + } + if (PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_StopIteration)) + break; + PyErr_Clear(); + } + /* iterator ended normally */ + x = v = POP(); + Py_DECREF(v); + JUMPBY(oparg); + continue; + + case BREAK_LOOP: + why = WHY_BREAK; + goto fast_block_end; + + case CONTINUE_LOOP: + retval = PyInt_FromLong(oparg); + if (!retval) { + x = NULL; + break; + } + why = WHY_CONTINUE; + goto fast_block_end; + + case SETUP_LOOP: + case SETUP_EXCEPT: + case SETUP_FINALLY: + /* NOTE: If you add any new block-setup opcodes that are not try/except/finally + handlers, you may need to update the PyGen_NeedsFinalizing() function. */ + + PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg, + STACK_LEVEL()); + continue; + + case WITH_CLEANUP: + { + /* TOP is the context.__exit__ bound method. + Below that are 1-3 values indicating how/why + we entered the finally clause: + - SECOND = None + - (SECOND, THIRD) = (WHY_{RETURN,CONTINUE}), retval + - SECOND = WHY_*; no retval below it + - (SECOND, THIRD, FOURTH) = exc_info() + In the last case, we must call + TOP(SECOND, THIRD, FOURTH) + otherwise we must call + TOP(None, None, None) + + In addition, if the stack represents an exception, + *and* the function call returns a 'true' value, we + "zap" this information, to prevent END_FINALLY from + re-raising the exception. (But non-local gotos + should still be resumed.) + */ + + x = TOP(); + u = SECOND(); + if (PyInt_Check(u) || u == Py_None) { + u = v = w = Py_None; + } + else { + v = THIRD(); + w = FOURTH(); + } + /* XXX Not the fastest way to call it... */ + x = PyObject_CallFunctionObjArgs(x, u, v, w, NULL); + if (x == NULL) + break; /* Go to error exit */ + if (u != Py_None && PyObject_IsTrue(x)) { + /* There was an exception and a true return */ + Py_DECREF(x); + x = TOP(); /* Again */ + STACKADJ(-3); + Py_INCREF(Py_None); + SET_TOP(Py_None); + Py_DECREF(x); + Py_DECREF(u); + Py_DECREF(v); + Py_DECREF(w); + } else { + /* Let END_FINALLY do its thing */ + Py_DECREF(x); + x = POP(); + Py_DECREF(x); + } + break; + } + + case CALL_FUNCTION: + { + PyObject **sp; + PCALL(PCALL_ALL); + sp = stack_pointer; +#ifdef WITH_TSC + x = call_function(&sp, oparg, &intr0, &intr1); +#else + x = call_function(&sp, oparg); +#endif + stack_pointer = sp; + PUSH(x); + if (x != NULL) + continue; + break; + } + + case CALL_FUNCTION_VAR: + case CALL_FUNCTION_KW: + case CALL_FUNCTION_VAR_KW: + { + int na = oparg & 0xff; + int nk = (oparg>>8) & 0xff; + int flags = (opcode - CALL_FUNCTION) & 3; + int n = na + 2 * nk; + PyObject **pfunc, *func, **sp; + PCALL(PCALL_ALL); + if (flags & CALL_FLAG_VAR) + n++; + if (flags & CALL_FLAG_KW) + n++; + pfunc = stack_pointer - n - 1; + func = *pfunc; + + if (PyMethod_Check(func) + && PyMethod_GET_SELF(func) != NULL) { + PyObject *self = PyMethod_GET_SELF(func); + Py_INCREF(self); + func = PyMethod_GET_FUNCTION(func); + Py_INCREF(func); + Py_DECREF(*pfunc); + *pfunc = self; + na++; + n++; + } else + Py_INCREF(func); + sp = stack_pointer; + READ_TIMESTAMP(intr0); + x = ext_do_call(func, &sp, flags, na, nk); + READ_TIMESTAMP(intr1); + stack_pointer = sp; + Py_DECREF(func); + + while (stack_pointer > pfunc) { + w = POP(); + Py_DECREF(w); + } + PUSH(x); + if (x != NULL) + continue; + break; + } + + case MAKE_FUNCTION: + v = POP(); /* code object */ + x = PyFunction_New(v, f->f_globals); + Py_DECREF(v); + /* XXX Maybe this should be a separate opcode? */ + if (x != NULL && oparg > 0) { + v = PyTuple_New(oparg); + if (v == NULL) { + Py_DECREF(x); + x = NULL; + break; + } + while (--oparg >= 0) { + w = POP(); + PyTuple_SET_ITEM(v, oparg, w); + } + err = PyFunction_SetDefaults(x, v); + Py_DECREF(v); + } + PUSH(x); + break; + + case MAKE_CLOSURE: + { + v = POP(); /* code object */ + x = PyFunction_New(v, f->f_globals); + Py_DECREF(v); + if (x != NULL) { + v = POP(); + err = PyFunction_SetClosure(x, v); + Py_DECREF(v); + } + if (x != NULL && oparg > 0) { + v = PyTuple_New(oparg); + if (v == NULL) { + Py_DECREF(x); + x = NULL; + break; + } + while (--oparg >= 0) { + w = POP(); + PyTuple_SET_ITEM(v, oparg, w); + } + err = PyFunction_SetDefaults(x, v); + Py_DECREF(v); + } + PUSH(x); + break; + } + + case BUILD_SLICE: + if (oparg == 3) + w = POP(); + else + w = NULL; + v = POP(); + u = TOP(); + x = PySlice_New(u, v, w); + Py_DECREF(u); + Py_DECREF(v); + Py_XDECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case EXTENDED_ARG: + opcode = NEXTOP(); + oparg = oparg<<16 | NEXTARG(); + goto dispatch_opcode; + + default: + fprintf(stderr, + "XXX lineno: %d, opcode: %d\n", + PyCode_Addr2Line(f->f_code, f->f_lasti), + opcode); + PyErr_SetString(PyExc_SystemError, "unknown opcode"); + why = WHY_EXCEPTION; + break; + +#ifdef CASE_TOO_BIG + } +#endif + + } /* switch */ + + on_error: + + READ_TIMESTAMP(inst1); + + /* Quickly continue if no error occurred */ + + if (why == WHY_NOT) { + if (err == 0 && x != NULL) { +#ifdef CHECKEXC + /* This check is expensive! */ + if (PyErr_Occurred()) + fprintf(stderr, + "XXX undetected error\n"); + else { +#endif + READ_TIMESTAMP(loop1); + continue; /* Normal, fast path */ +#ifdef CHECKEXC + } +#endif + } + why = WHY_EXCEPTION; + x = Py_None; + err = 0; + } + + /* Double-check exception status */ + + if (why == WHY_EXCEPTION || why == WHY_RERAISE) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_SystemError, + "error return without exception set"); + why = WHY_EXCEPTION; + } + } +#ifdef CHECKEXC + else { + /* This check is expensive! */ + if (PyErr_Occurred()) { + char buf[1024]; + sprintf(buf, "Stack unwind with exception " + "set and why=%d", why); + Py_FatalError(buf); + } + } +#endif + + /* Log traceback info if this is a real exception */ + + if (why == WHY_EXCEPTION) { + PyTraceBack_Here(f); + + if (tstate->c_tracefunc != NULL) + call_exc_trace(tstate->c_tracefunc, + tstate->c_traceobj, f); + } + + /* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */ + + if (why == WHY_RERAISE) + why = WHY_EXCEPTION; + + /* Unwind stacks if a (pseudo) exception occurred */ + +fast_block_end: + while (why != WHY_NOT && f->f_iblock > 0) { + PyTryBlock *b = PyFrame_BlockPop(f); + + assert(why != WHY_YIELD); + if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) { + /* For a continue inside a try block, + don't pop the block for the loop. */ + PyFrame_BlockSetup(f, b->b_type, b->b_handler, + b->b_level); + why = WHY_NOT; + JUMPTO(PyInt_AS_LONG(retval)); + Py_DECREF(retval); + break; + } + + while (STACK_LEVEL() > b->b_level) { + v = POP(); + Py_XDECREF(v); + } + if (b->b_type == SETUP_LOOP && why == WHY_BREAK) { + why = WHY_NOT; + JUMPTO(b->b_handler); + break; + } + if (b->b_type == SETUP_FINALLY || + (b->b_type == SETUP_EXCEPT && + why == WHY_EXCEPTION)) { + if (why == WHY_EXCEPTION) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (val == NULL) { + val = Py_None; + Py_INCREF(val); + } + /* Make the raw exception data + available to the handler, + so a program can emulate the + Python main loop. Don't do + this for 'finally'. */ + if (b->b_type == SETUP_EXCEPT) { + PyErr_NormalizeException( + &exc, &val, &tb); + set_exc_info(tstate, + exc, val, tb); + } + if (tb == NULL) { + Py_INCREF(Py_None); + PUSH(Py_None); + } else + PUSH(tb); + PUSH(val); + PUSH(exc); + } + else { + if (why & (WHY_RETURN | WHY_CONTINUE)) + PUSH(retval); + v = PyInt_FromLong((long)why); + PUSH(v); + } + why = WHY_NOT; + JUMPTO(b->b_handler); + break; + } + } /* unwind stack */ + + /* End the loop if we still have an error (or return) */ + + if (why != WHY_NOT) + break; + READ_TIMESTAMP(loop1); + + } /* main loop */ + + assert(why != WHY_YIELD); + /* Pop remaining stack entries. */ + while (!EMPTY()) { + v = POP(); + Py_XDECREF(v); + } + + if (why != WHY_RETURN) + retval = NULL; + +fast_yield: + if (tstate->use_tracing) { + if (tstate->c_tracefunc) { + if (why == WHY_RETURN || why == WHY_YIELD) { + if (call_trace(tstate->c_tracefunc, + tstate->c_traceobj, f, + PyTrace_RETURN, retval)) { + Py_XDECREF(retval); + retval = NULL; + why = WHY_EXCEPTION; + } + } + else if (why == WHY_EXCEPTION) { + call_trace_protected(tstate->c_tracefunc, + tstate->c_traceobj, f, + PyTrace_RETURN, NULL); + } + } + if (tstate->c_profilefunc) { + if (why == WHY_EXCEPTION) + call_trace_protected(tstate->c_profilefunc, + tstate->c_profileobj, f, + PyTrace_RETURN, NULL); + else if (call_trace(tstate->c_profilefunc, + tstate->c_profileobj, f, + PyTrace_RETURN, retval)) { + Py_XDECREF(retval); + retval = NULL; + why = WHY_EXCEPTION; + } + } + } + + if (tstate->frame->f_exc_type != NULL) + reset_exc_info(tstate); + else { + assert(tstate->frame->f_exc_value == NULL); + assert(tstate->frame->f_exc_traceback == NULL); + } + + /* pop frame */ + exit_eval_frame: + Py_LeaveRecursiveCall(); + tstate->frame = f->f_back; + + return retval; +} + diff --git a/tests/examplefiles/cheetah_example.html b/tests/examplefiles/cheetah_example.html new file mode 100644 index 0000000..e2a0f47 --- /dev/null +++ b/tests/examplefiles/cheetah_example.html @@ -0,0 +1,13 @@ + + $title + + + #for $client in $clients + + + + + #end for +
$client.surname, $client.firstname$client.email
+ + diff --git a/tests/examplefiles/classes.dylan b/tests/examplefiles/classes.dylan new file mode 100644 index 0000000..a77956d --- /dev/null +++ b/tests/examplefiles/classes.dylan @@ -0,0 +1,20 @@ +define class () + slot serial-number :: = unique-serial-number(); + slot model-name :: , + required-init-keyword: model:; + slot has-sunroof? :: , + init-keyword: sunroof?:, + init-value: #f; +end class ; + +define variable *unique-serial-number* = 0; + +define function unique-serial-number() => (usn :: ) + let serial = *unique-serial-number*; + *unique-serial-number* := *unique-serial-number* + 1; + serial; +end function; + +define constant $blue-car = make(, model: "Viper"); +define constant $black-car = make(, model: "Town Car", sunroof?: #t); +define constant $red-car = make(, model: "F40", sunroof?: #f); diff --git a/tests/examplefiles/condensed_ruby.rb b/tests/examplefiles/condensed_ruby.rb new file mode 100644 index 0000000..afe57aa --- /dev/null +++ b/tests/examplefiles/condensed_ruby.rb @@ -0,0 +1,10 @@ +# Server: ruby p2p.rb password server server-uri merge-servers +# Sample: ruby p2p.rb foobar server druby://localhost:1337 druby://foo.bar:1337 +# Client: ruby p2p.rb password client server-uri download-pattern +# Sample: ruby p2p.rb foobar client druby://localhost:1337 *.rb +require'drb';F,D,C,P,M,U,*O=File,Class,Dir,*ARGV;def s(p)F.split(p[/[^|].*/])[-1 +]end;def c(u);DRbObject.new((),u)end;def x(u)[P,u].hash;end;M=="client"&&c(U).f( +x(U)).each{|n|p,c=x(n),c(n);(c.f(p,O[0],0).map{|f|s f}-D["*"]).each{|f|F.open(f, +"w"){|o|o<\n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Language: Deutsch\n" +"X-Language-in-English: German\n" +"X-HasWikiMarkup: True\n" +"X-Direction: ltr\n" + +msgid "" +"This wiki is not enabled for mail processing.\n" +"Contact the owner of the wiki, who can enable email." +msgstr "" +"In diesem Wiki ist Mail-Verarbeitung nicht eingeschaltet.\n" +"Bitte kontaktieren Sie den Eigentümer des Wiki, der die Mailfunktionen " +"einschalten kann." + +msgid "Please provide a valid email address!" +msgstr "Bitte eine gültige E-Mail-Adresse angeben!" + +#, python-format +msgid "Found no account matching the given email address '%(email)s'!" +msgstr "" +"Es wurde kein Benutzerkonto mit der E-Mail-Adresse '%(email)s' gefunden!" + +msgid "Use UserPreferences to change your settings or create an account." +msgstr "" +"Benutzen Sie BenutzerEinstellungen, um Ihre Einstellungen zu ändern oder ein " +"Konto zu erzeugen." + +msgid "Empty user name. Please enter a user name." +msgstr "Leerer Benutzername, bitte geben Sie einen Benutzernamen ein." + +#, python-format +msgid "" +"Invalid user name {{{'%s'}}}.\n" +"Name may contain any Unicode alpha numeric character, with optional one\n" +"space between words. Group page name is not allowed." +msgstr "" +"Ungültiger Benutzername {{{'%s'}}}.\n" +"Der Name darf beliebige alphanumerische Unicode-Zeichen enthalten, mit " +"optional einem\n" +"Leerzeichen zwischen den Worten. Gruppennamen sind nicht erlaubt." + +msgid "This user name already belongs to somebody else." +msgstr "Dieser Benutzername gehört bereits jemand anderem." + +msgid "Passwords don't match!" +msgstr "Die Passworte sind nicht gleich!" + +msgid "Please specify a password!" +msgstr "Bitte geben Sie ein Passwort an!" + +msgid "" +"Please provide your email address. If you lose your login information, you " +"can get it by email." +msgstr "" +"Bitte geben Sie Ihre E-Mail-Adresse an. Wenn Sie Ihre Login-Informationen " +"verlieren können Sie sie per E-Mail wieder bekommen." + +msgid "This email already belongs to somebody else." +msgstr "Diese E-Mail-Adresse gehört bereits jemand anderem." + +msgid "User account created! You can use this account to login now..." +msgstr "" +"Es wurde ein Benutzerkonto für Sie angelegt. Sie können sich nun anmelden..." + +msgid "Use UserPreferences to change settings of the selected user account" +msgstr "" +"Benutzen Sie BenutzerEinstellungen, um die Einstellungen des ausgewählten " +"Benutzers zu ändern." + +#, python-format +msgid "The theme '%(theme_name)s' could not be loaded!" +msgstr "Das Theme '%(theme_name)s' konnte nicht geladen werden!" + +msgid "User preferences saved!" +msgstr "Persönliche Einstellungen gespeichert!" + +msgid "Default" +msgstr "Standardeinstellung" + +msgid "" +msgstr "" + +msgid "the one preferred" +msgstr "der Bevorzugte" + +msgid "free choice" +msgstr "Freie Auswahl" + +msgid "Select User" +msgstr "Benutzer auswählen" + +msgid "Save" +msgstr "Speichern" + +msgid "Cancel" +msgstr "Abbrechen" + +msgid "Preferred theme" +msgstr "Bevorzugter Stil" + +msgid "Editor Preference" +msgstr "Bevorzugter Editor" + +msgid "Editor shown on UI" +msgstr "Angezeigter Editor" + +msgid "Time zone" +msgstr "Zeitzone" + +msgid "Your time is" +msgstr "Die lokale Zeit ist" + +msgid "Server time is" +msgstr "Die Zeit des Servers ist" + +msgid "Date format" +msgstr "Datumsformat" + +msgid "Preferred language" +msgstr "Bevorzugte Sprache" + +msgid "General options" +msgstr "Allgemeine Optionen" + +msgid "Quick links" +msgstr "Expressverweise" + +msgid "This list does not work, unless you have entered a valid email address!" +msgstr "" +"Änderungsnachrichten werden nur versandt, wenn eine gültige E-Mail-Adresse " +"eingegeben wurde!" + +msgid "Subscribed wiki pages (one regex per line)" +msgstr "Abonnierte Wiki-Seiten (ein regulärer Ausdruck pro Zeile)" + +msgid "Create Profile" +msgstr "Benutzer anlegen" + +msgid "Mail me my account data" +msgstr "E-Mail mit den Zugangsdaten senden" + +msgid "Email" +msgstr "E-Mail" + +#, python-format +msgid "" +"To create an account, see the %(userprefslink)s page. To recover a lost " +"password, go to %(sendmypasswordlink)s." +msgstr "" +"Siehe Seite %(userprefslink)s, um einen Account anzulegen. Um ein verlorenes " +"Passwort wieder zu erhalten, siehe %(sendmypasswordlink)s." + +msgid "Name" +msgstr "Name" + +msgid "Password" +msgstr "Passwort" + +msgid "Login" +msgstr "Anmelden" + +msgid "Action" +msgstr "Aktion" + +#, python-format +msgid "Expected \"=\" to follow \"%(token)s\"" +msgstr "\"=\" fehlt hinter dem Attribut \"%(token)s\"" + +#, python-format +msgid "Expected a value for key \"%(token)s\"" +msgstr "Attribut \"%(token)s\" wurde kein Wert zugewiesen" + +msgid "You are not allowed to edit this page." +msgstr "Sie dürfen diese Seite nicht editieren." + +msgid "Page is immutable!" +msgstr "Die Seite ist gegen Änderungen geschützt!" + +msgid "Cannot edit old revisions!" +msgstr "Alte Versionen können nicht editiert werden!" + +msgid "The lock you held timed out. Be prepared for editing conflicts!" +msgstr "" +"Die von Ihnen gehaltene Sperre ist abgelaufen. Das Auftreten von " +"Änderungskonflikten ist wahrscheinlich!" + +#, python-format +msgid "Draft of \"%(pagename)s\"" +msgstr "Entwurf von \"%(pagename)s\"" + +#, python-format +msgid "Edit \"%(pagename)s\"" +msgstr "\"%(pagename)s\" editieren" + +#, python-format +msgid "Preview of \"%(pagename)s\"" +msgstr "Vorschau für \"%(pagename)s\"" + +#, python-format +msgid "Your edit lock on %(lock_page)s has expired!" +msgstr "Ihre Sperre der Seite %(lock_page)s ist abgelaufen!" + +#, python-format +msgid "Your edit lock on %(lock_page)s will expire in # minutes." +msgstr "Ihre Sperre der Seite %(lock_page)s läuft in # Minuten ab." + +#, python-format +msgid "Your edit lock on %(lock_page)s will expire in # seconds." +msgstr "Ihre Sperre der Seite %(lock_page)s läuft in # Sekunden ab." + +msgid "Someone else deleted this page while you were editing!" +msgstr "Ein anderer Benutzer hat diese Seite inzwischen gelöscht!" + +msgid "Someone else changed this page while you were editing!" +msgstr "Ein anderer Benutzer hat diese Seite inzwischen geändert!" + +msgid "" +"Someone else saved this page while you were editing!\n" +"Please review the page and save then. Do not save this page as it is!" +msgstr "" +"Ein anderer Benutzer hat gespeichert, während Sie editiert haben!\n" +"Bitte schauen Sie die Seite nochmal durch und speichern Sie dann. Speichern " +"Sie die Seite nicht so, wie sie ist!" + +msgid "[Content loaded from draft]" +msgstr "[Inhalt der Seite mit dem Entwurf geladen]" + +#, python-format +msgid "[Content of new page loaded from %s]" +msgstr "[Inhalt der neuen Seite auf Basis der Vorlage %s]" + +#, python-format +msgid "[Template %s not found]" +msgstr "[Vorlage %s nicht gefunden]" + +#, python-format +msgid "[You may not read %s]" +msgstr "[Sie dürfen %s nicht lesen]" + +#, python-format +msgid "" +"'''[[BR]]Your draft based on revision %(draft_rev)d (saved %" +"(draft_timestamp_str)s) can be loaded instead of the current revision %" +"(page_rev)d by using the load draft button - in case you lost your last edit " +"somehow without saving it.''' A draft gets saved for you when you do a " +"preview, cancel an edit or unsuccessfully save." +msgstr "" +"'''[[BR]]Ihr Entwurf basierend auf Revision %(draft_rev)d (gespeichert %" +"(draft_timestamp_str)s kann anstatt der aktuellen Revision %(page_rev)d " +"geladen werden, indem Sie den Knopf ''Entwurf laden'' drücken (falls Sie " +"Ihre letzten Änderungen verloren haben, bevor Sie sie gespeichert " +"hatten).''' Ein Entwurf wird für Sie gespeichert, wenn Sie auf Vorschau oder " +"Abbrechen drücken oder das Speichern nicht funktioniert." + +#, python-format +msgid "Describe %s here." +msgstr "%s hier beschreiben..." + +msgid "Check Spelling" +msgstr "Rechtschreibung prüfen" + +msgid "Save Changes" +msgstr "Änderungen speichern" + +#, python-format +msgid "" +"By hitting '''%(save_button_text)s''' you put your changes under the %" +"(license_link)s.\n" +"If you don't want that, hit '''%(cancel_button_text)s''' to cancel your " +"changes." +msgstr "" +"Durch Anklicken von '''%(save_button_text)s''' stellen Sie Ihre Änderungen " +"unter die %(license_link)s.\n" +"Wenn Sie das nicht wollen, klicken Sie auf '''%(cancel_button_text)s''', um " +"Ihre Änderungen zu verwerfen." + +msgid "Preview" +msgstr "Vorschau anzeigen" + +msgid "Text mode" +msgstr "Text-Modus" + +msgid "Load Draft" +msgstr "Entwurf laden" + +msgid "Comment:" +msgstr "Kommentar:" + +msgid "" +msgstr "" + +#, python-format +msgid "Add to: %(category)s" +msgstr "Zu %(category)s hinzufügen:" + +msgid "Trivial change" +msgstr "Triviale Änderung" + +msgid "Remove trailing whitespace from each line" +msgstr "Leerzeichen am Ende jeder Zeile entfernen" + +msgid "The wiki is currently not reachable." +msgstr "Das Wiki ist derzeit nicht erreichbar." + +msgid "" +"The remote version of MoinMoin is too old, version 1.6 is required at least." +msgstr "" +"Die ferne MoinMoin-Version ist zu alt, mindestens Version 1.6 wird benötigt." + +msgid "Invalid username or password." +msgstr "Ungültiger Username oder Passwort." + +#, python-format +msgid "" +"The remote wiki uses a different InterWiki name (%(remotename)s) internally " +"than you specified (%(localname)s)." +msgstr "" +"Das ferne Wiki benutzt intern einen anderen InterWiki-Namen (%(remotename)s) " +"als Sie angegeben haben (%(localname)s)." + +#, python-format +msgid "The package needs a newer version of MoinMoin (at least %s)." +msgstr "Das Paket erfordert eine neuere Version von MoinMoin (mindestens %s)." + +msgid "The theme name is not set." +msgstr "Theme-Name ist nicht gesetzt." + +msgid "Installing theme files is only supported for standalone type servers." +msgstr "" +"Das Installieren von Theme-Dateien wird nur für Server-Typ standalone " +"unterstützt." + +#, python-format +msgid "Installation of '%(filename)s' failed." +msgstr "Installation von '%(filename)s' fehlgeschlagen." + +#, python-format +msgid "The file %s is not a MoinMoin package file." +msgstr "Die Datei %s ist keine MoinMoin-Paket-Datei." + +#, python-format +msgid "The page %s does not exist." +msgstr "Die Seite %s existiert nicht." + +msgid "Invalid package file header." +msgstr "Ungültiger Paket-Datei-Header." + +msgid "Package file format unsupported." +msgstr "Paket-Datei-Format nicht unterstützt." + +#, python-format +msgid "Unknown function %(func)s in line %(lineno)i." +msgstr "Unbekannte Funktion %(func)s in Zeile %(lineno)i." + +#, python-format +msgid "The file %s was not found in the package." +msgstr "Die Datei %s wurde im Paket nicht gefunden." + +msgid "Your changes are not saved!" +msgstr "Ihre Änderungen sind nicht gesichert!" + +msgid "Page name is too long, try shorter name." +msgstr "Seitenname ist zu lang, bitte kürzen." + +msgid "GUI Mode" +msgstr "GUI-Modus" + +msgid "Edit was cancelled." +msgstr "Editierung wurde abgebrochen." + +msgid "You can't copy to an empty pagename." +msgstr "Sie können eine Seite nicht auf einen leeren Seitennamen kopieren." + +msgid "You are not allowed to copy this page!" +msgstr "Sie dürfen diese Seite nicht kopieren!" + +#, python-format +msgid "" +"'''A page with the name {{{'%s'}}} already exists.'''\n" +"Try a different name." +msgstr "" +"'''Es gibt bereits eine Seite mit dem Namen {{{'%s'}}}.'''\n" +"Versuchen Sie es mit einem anderen Namen." + +#, python-format +msgid "Could not copy page because of file system error: %s." +msgstr "" +"Konnte die Seite nicht kopieren wegen eines Dateisystem-Fehlercodes: %s." + +msgid "You are not allowed to rename this page!" +msgstr "Sie dürfen diese Seite nicht umbenennen!" + +msgid "You can't rename to an empty pagename." +msgstr "Sie können eine Seite nicht auf einen leeren Seitennamen umbenennen." + +#, python-format +msgid "" +"'''A page with the name {{{'%s'}}} already exists.'''\n" +"\n" +"Try a different name." +msgstr "" +"'''Es gibt bereits eine Seite mit dem Namen {{{'%s'}}}.'''\n" +"Versuchen Sie es mit einem anderen Namen." + +#, python-format +msgid "Could not rename page because of file system error: %s." +msgstr "" +"Konnte die Seite nicht umbenennen wegen eines Dateisystem-Fehlercodes: %s." + +msgid "You are not allowed to delete this page!" +msgstr "Sie dürfen diese Seite nicht löschen!" + +msgid "Thank you for your changes. Your attention to detail is appreciated." +msgstr "Danke für die Änderung und die Sorgfalt beim Editieren." + +#, python-format +msgid "Page \"%s\" was successfully deleted!" +msgstr "Seite \"%s\" wurde erfolgreich gelöscht!" + +#, python-format +msgid "" +"Dear Wiki user,\n" +"\n" +"You have subscribed to a wiki page or wiki category on \"%(sitename)s\" for " +"change notification.\n" +"\n" +"The following page has been changed by %(editor)s:\n" +"%(pagelink)s\n" +"\n" +msgstr "" +"Sehr geehrter Wikibenutzer,\n" +"\n" +"Sie haben die Änderungen einer Wikiseite oder Kategorie von \"%(sitename)s\" " +"abonniert.\n" +"\n" +"Die folgende Seite wurde durch %(editor)s verändert:\n" +"%(pagelink)s\n" +"\n" + +#, python-format +msgid "" +"The comment on the change is:\n" +"%(comment)s\n" +"\n" +msgstr "" +"Der Kommentar zur Änderung ist:\n" +"%(comment)s\n" +"\n" + +msgid "New page:\n" +msgstr "Neue Seite:\n" + +msgid "No differences found!\n" +msgstr "Es wurden keine Änderungen gefunden!\n" + +#, python-format +msgid "[%(sitename)s] %(trivial)sUpdate of \"%(pagename)s\" by %(username)s" +msgstr "" +"[%(sitename)s] %(trivial)sÄnderung von \"%(pagename)s\" von %(username)s" + +msgid "Trivial " +msgstr "Triviale " + +msgid "Status of sending notification mails:" +msgstr "Status des Versands der Änderungsnachrichten:" + +#, python-format +msgid "[%(lang)s] %(recipients)s: %(status)s" +msgstr "[%(lang)s] %(recipients)s: %(status)s" + +#, python-format +msgid "Page could not get locked. Unexpected error (errno=%d)." +msgstr "Seite konnte nicht gesperrt werden. Unerwarteter Fehler (errno=%d)." + +msgid "Page could not get locked. Missing 'current' file?" +msgstr "Seite konnte nicht gesperrt werden. Fehlende Datei 'current'?" + +msgid "You are not allowed to edit this page!" +msgstr "Sie dürfen diese Seite nicht editieren!" + +msgid "You cannot save empty pages." +msgstr "Leere Seiten können nicht gespeichert werden!" + +msgid "You already saved this page!" +msgstr "Sie haben diese Seite bereits gesichert!" + +msgid "You already edited this page! Please do not use the back button." +msgstr "" +"Sie haben diese Seite bereits editiert! Bitte benutzen Sie nicht den Zurück-" +"Button." + +msgid "You did not change the page content, not saved!" +msgstr "Der Seiteninhalt wurde nicht verändert und folglich nicht gesichert!" + +msgid "" +"You can't change ACLs on this page since you have no admin rights on it!" +msgstr "" +"Sie dürfen keine ACLs auf dieser Seite ändern, weil Sie keine admin-Rechte " +"auf ihr haben!" + +#, python-format +msgid "" +"The lock of %(owner)s timed out %(mins_ago)d minute(s) ago, and you were " +"granted the lock for this page." +msgstr "" +"Die Sperre von %(owner)s ist vor %(mins_ago)d Minute(n) abgelaufen und wurde " +"an Sie übertragen." + +#, python-format +msgid "" +"Other users will be ''blocked'' from editing this page until %(bumptime)s." +msgstr "" +"Anderen Benutzern wird die Editierung dieser Seite bis %(bumptime)s " +"''verweigert''." + +#, python-format +msgid "" +"Other users will be ''warned'' until %(bumptime)s that you are editing this " +"page." +msgstr "" +"Andere Benutzer erhalten bis %(bumptime)s eine ''Warnung'', dass Sie diese " +"Seite editieren." + +msgid "Use the Preview button to extend the locking period." +msgstr "Mit \"Vorschau anzeigen\" können Sie diesen Zeitraum verlängern." + +#, python-format +msgid "" +"This page is currently ''locked'' for editing by %(owner)s until %(timestamp)" +"s, i.e. for %(mins_valid)d minute(s)." +msgstr "" +"Diese Seite ist derzeit zur Editierung durch %(owner)s gegen Änderungen " +"''gesperrt'' bis %(timestamp)s, also weitere %(mins_valid)d Minute(n)." + +#, python-format +msgid "" +"This page was opened for editing or last previewed at %(timestamp)s by %" +"(owner)s.[[BR]]\n" +"'''You should ''refrain from editing'' this page for at least another %" +"(mins_valid)d minute(s),\n" +"to avoid editing conflicts.'''[[BR]]\n" +"To leave the editor, press the Cancel button." +msgstr "" +"Diese Seite wurde zum letzten Mal um %(timestamp)s durch %(owner)s zum " +"Editieren geöffnet\n" +"oder in der Vorschau angezeigt.[[BR]]\n" +"'''Sie sollten diese Seite für mindestens weitere %(mins_valid)d Minute(n) " +"''nicht editieren'', um Konflikte auszuschließen.'''[[BR]]\n" +"Benutzen Sie \"Abbrechen\" zum Verlassen des Editors." + +msgid "" +msgstr "" + +#, python-format +msgid "" +"Login Name: %s\n" +"\n" +"Login Password: %s\n" +"\n" +"Login URL: %s/%s?action=login\n" +msgstr "" +"Anmelde-Name: %s\n" +"\n" +"Anmelde-Passwort: %s\n" +"\n" +"Anmelde-URL: %s/%s?action=login\n" + +msgid "" +"Somebody has requested to submit your account data to this email address.\n" +"\n" +"If you lost your password, please use the data below and just enter the\n" +"password AS SHOWN into the wiki's password form field (use copy and paste\n" +"for that).\n" +"\n" +"After successfully logging in, it is of course a good idea to set a new and " +"known password.\n" +msgstr "" +"Jemand hat angefordert, Ihre Accountdaten an diese E-Mail-Adresse zu " +"senden.\n" +"\n" +"Wenn Sie Ihr Passwort vergessen haben, benutzen Sie bitte die Daten unten " +"und\n" +"geben Sie das Passwort GENAUSO WIE ANGEZEIGT in das Passwort-Feld des Wikis " +"ein (benutzen Sie kopieren und einfügen dazu).\n" +"\n" +"Nachdem Sie sich erfolgreich angemeldet haben, setzen Sie bitte Ihr Passwort " +"neu.\n" + +#, python-format +msgid "[%(sitename)s] Your wiki account data" +msgstr "[%(sitename)s] Ihre Wiki-Acount-Daten" + +msgid "" +"The backed up content of this page is deprecated and will not be included in " +"search results!" +msgstr "" +"Der Inhalt der letzten Sicherungskopie ist veraltet und wird von der " +"Volltextsuche ignoriert!" + +#, python-format +msgid "Revision %(rev)d as of %(date)s" +msgstr "Revision %(rev)d vom %(date)s" + +#, python-format +msgid "Redirected from page \"%(page)s\"" +msgstr "Hierher umgeleitet von Seite \"%(page)s\"" + +#, python-format +msgid "This page redirects to page \"%(page)s\"" +msgstr "Diese Seite wird umgeleitet auf \"%(page)s\"" + +msgid "Create New Page" +msgstr "Neue Seite anlegen" + +msgid "You are not allowed to view this page." +msgstr "Sie dürfen diese Seite nicht ansehen." + +#, python-format +msgid "" +"Results %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s of %(aboutHits)s %(bs)s%(hits)d" +"%(be)s results out of about %(pages)d pages." +msgstr "" +"Ergebnisse %(bs)s%(hitsFrom)d - %(hitsTo)d%(be)s von %(aboutHits)s %(bs)s%" +"(hits)d%(be)s Ergebnisse aus ungefähr %(pages)d Seiten." + +msgid "seconds" +msgstr "Sekunden" + +msgid "Previous" +msgstr "Vorherige" + +msgid "Next" +msgstr "Nächste" + +msgid "current" +msgstr "aktuelle" + +#, python-format +msgid "last modified: %s" +msgstr "zuletzt geändert: %s" + +msgid "match" +msgstr "Treffer" + +msgid "matches" +msgstr "Treffer" + +msgid "Go To Page" +msgstr "Gehe zu Seite" + +msgid "Include system pages" +msgstr "Systemseiten einschließen" + +msgid "Exclude system pages" +msgstr "Systemseiten ausschließen" + +#, python-format +msgid "Please use a more selective search term instead of {{{\"%s\"}}}" +msgstr "" +"Bitte verwenden Sie einen selektiveren Suchbegriff anstatt {{{\"%s\"}}}" + +#, python-format +msgid "ERROR in regex '%s'" +msgstr "FEHLER in regulärem Ausdruck '%s'" + +#, python-format +msgid "Bad timestamp '%s'" +msgstr "Ungültige Zeitangabe '%s'" + +#, python-format +msgid "Unsupported navigation scheme '%(scheme)s'!" +msgstr "Nicht bekanntes Navigationsschema '%(scheme)s'!" + +msgid "No parent page found!" +msgstr "Diese Seite ist keine Unterseite!" + +msgid "Wiki" +msgstr "Wiki" + +msgid "Edit" +msgstr "Editieren" + +msgid "Slideshow" +msgstr "Diaschau" + +msgid "Start" +msgstr "Start" + +#, python-format +msgid "Slide %(pos)d of %(size)d" +msgstr "Seite %(pos)d von %(size)d" + +msgid "Search Titles" +msgstr "Titel durchsuchen" + +msgid "Display context of search results" +msgstr "Umgebung der Treffer anzeigen" + +msgid "Case-sensitive searching" +msgstr "Groß-/Kleinschreibung beachten" + +msgid "Search Text" +msgstr "Text durchsuchen" + +#, python-format +msgid "Not supported mimetype of file: %s" +msgstr "MIME-Typ der Datei wird nicht unterstützt: %s" + +msgid "Embedded" +msgstr "Eingebettet" + +#, python-format +msgid "Upload new attachment \"%(filename)s\"" +msgstr "Neuen Dateianhang \"%(filename)s\" hochladen" + +#, python-format +msgid "Invalid MonthCalendar calparms \"%s\"!" +msgstr "Ungültige MonthCalendaer calparms \"%s\"!" + +#, python-format +msgid "Invalid MonthCalendar arguments \"%s\"!" +msgstr "Ungültige MonthCalendar-Argumente: \"%s\"!" + +msgid "No orphaned pages in this wiki." +msgstr "Es existieren keine verwaisten Seiten in diesem Wiki." + +msgid "Python Version" +msgstr "Python Version" + +msgid "MoinMoin Version" +msgstr "MoinMoin Version" + +#, python-format +msgid "Release %s [Revision %s]" +msgstr "Version %s [Revision %s]" + +msgid "4Suite Version" +msgstr "4Suite Version" + +msgid "Number of pages" +msgstr "Seitenanzahl" + +msgid "Number of system pages" +msgstr "Anzahl der Systemseiten" + +msgid "Accumulated page sizes" +msgstr "Kumulierte Seitengrößen" + +#, python-format +msgid "Disk usage of %(data_dir)s/pages/" +msgstr "Plattenbelegung von %(data_dir)s/pages/" + +#, python-format +msgid "Disk usage of %(data_dir)s/" +msgstr "Plattenbelegung von %(data_dir)s/" + +msgid "Entries in edit log" +msgstr "Einträge in der Änderungshistorie" + +msgid "NONE" +msgstr "KEINE" + +msgid "Global extension macros" +msgstr "Globale Erweiterungsmakros" + +msgid "Local extension macros" +msgstr "Lokale Erweiterungsmakros" + +msgid "Global extension actions" +msgstr "Globale Erweiterungsaktionen" + +msgid "Local extension actions" +msgstr "Lokale Erweiterungsaktionen" + +msgid "Global parsers" +msgstr "Globale Parser" + +msgid "Local extension parsers" +msgstr "Lokale Erweiterungsparser" + +msgid "Disabled" +msgstr "Deaktiviert" + +msgid "Enabled" +msgstr "Aktiviert" + +msgid "index available" +msgstr "Index verfügbar" + +msgid "index unavailable" +msgstr "Index nicht verfügbar" + +msgid "N/A" +msgstr "k.A." + +msgid "Xapian and/or Python Xapian bindings not installed" +msgstr "Xapian und/oder Python-Xapian-Bindings nicht installiert" + +msgid "Xapian search" +msgstr "Xapian-Suche" + +msgid "Xapian Version" +msgstr "Xapian-Version" + +msgid "Xapian stemming" +msgstr "Xapian-Wortstamm-Bildung" + +msgid "Active threads" +msgstr "Aktive Threads" + +#, python-format +msgid "No quotes on %(pagename)s." +msgstr "Keine Zitate auf Seite %(pagename)s gefunden." + +#, python-format +msgid "Upload of attachment '%(filename)s'." +msgstr "Dateianhang '%(filename)s' wurde angelegt." + +#, python-format +msgid "Attachment '%(filename)s' deleted." +msgstr "Dateianhang '%(filename)s' wurde gelöscht." + +#, python-format +msgid "Drawing '%(filename)s' saved." +msgstr "Zeichnung '%(filename)s' wurde gesichert." + +#, python-format +msgid "Revert to revision %(rev)d." +msgstr "Revision %(rev)d restauriert." + +#, python-format +msgid "Renamed from '%(oldpagename)s'." +msgstr "Umbenannt von '%(oldpagename)s'." + +#, python-format +msgid "%(mins)dm ago" +msgstr "vor %(mins)dm" + +msgid "(no bookmark set)" +msgstr "(kein Lesezeichen gesetzt)" + +#, python-format +msgid "(currently set to %s)" +msgstr "(derzeit %s)" + +msgid "Delete bookmark" +msgstr "Lesezeichen löschen" + +msgid "Set bookmark" +msgstr "Lesezeichen setzen" + +msgid "[Bookmark reached]" +msgstr "[Lesezeichen erreicht]" + +#, python-format +msgid "Invalid include arguments \"%s\"!" +msgstr "Ungültige \"Include\"-Argumente: \"%s\"!" + +#, python-format +msgid "Nothing found for \"%s\"!" +msgstr "Textmarkierung \"%s\" nicht gefunden!" + +msgid "edit" +msgstr "ändern" + +msgid "Contents" +msgstr "Inhaltsverzeichnis" + +msgid "You need to provide a chart type!" +msgstr "Es muss ein Diagrammtyp angegeben werden!" + +#, python-format +msgid "Bad chart type \"%s\"!" +msgstr "Unbekannter Diagrammtyp \"%s\"!" + +msgid "Search for items" +msgstr "Nach Items suchen" + +msgid "containing all the following terms" +msgstr "die alle folgenden Ausdrücke enthalten" + +msgid "containing one or more of the following terms" +msgstr "die einen oder mehrere der folgenden Ausdrücke enthalten" + +msgid "not containing the following terms" +msgstr "die folgende Ausdrücke nicht enthalten" + +msgid "belonging to one of the following categories" +msgstr "die einer der folgenden Kategorien angehören" + +msgid "last modified since (e.g. last 2 weeks)" +msgstr "die zuletzt geändert wurden seit (z.B. 'last 2 weeks')" + +msgid "any language" +msgstr "jede Sprache" + +msgid "any mimetype" +msgstr "jeder MIME-Typ" + +msgid "Language" +msgstr "Sprache" + +msgid "File Type" +msgstr "Dateityp" + +msgid "Search only in titles" +msgstr "Nur Titel durchsuchen" + +msgid "Case-sensitive search" +msgstr "Groß-/Kleinschreibung bei der Suche beachten" + +msgid "Exclude underlay" +msgstr "Underlay ausschließen" + +msgid "No system items" +msgstr "Keine System-Items" + +msgid "Search in all page revisions" +msgstr "In allen Seitenrevisionen suchen" + +msgid "Go get it!" +msgstr "Los geht's" + +#, python-format +msgid "Check your argument %s" +msgstr "Überprüfen Sie das Argument %s" + +msgid "Markup" +msgstr "Notation" + +msgid "Display" +msgstr "Anzeige" + +msgid "No wanted pages in this wiki." +msgstr "Es existieren keine gewünschten Seiten in diesem Wiki." + +#, python-format +msgid "Connection to mailserver '%(server)s' failed: %(reason)s" +msgstr "Verbindung zum Mailserver '%(server)s' gestört: %(reason)s" + +msgid "Mail not sent" +msgstr "E-Mail wurde nicht versandt" + +msgid "Mail sent OK" +msgstr "E-Mail wurde erfolgreich versandt" + +msgid "Date" +msgstr "Datum" + +msgid "From" +msgstr "Von" + +msgid "To" +msgstr "An" + +msgid "Content" +msgstr "Inhalt" + +msgid "Attachments" +msgstr "Dateianhänge" + +msgid "XSLT option disabled, please look at HelpOnConfiguration." +msgstr "XSLT-Option ist abgeschaltet, siehe HelpOnConfiguration." + +msgid "XSLT processing is not available, please install 4suite 1.x." +msgstr "" +"Die Verarbeitung von XSLT-Stylesheets ist nicht verfügbar, bitte 4suite 1.x " +"installieren." + +#, python-format +msgid "%(errortype)s processing error" +msgstr "Verarbeitungsfehler vom Typ \"%(errortype)s\"" + +#, python-format +msgid "Expected \"%(wanted)s\" after \"%(key)s\", got \"%(token)s\"" +msgstr "Erwartete \"%(wanted)s\" nach \"%(key)s\", bekam \"%(token)s\"" + +#, python-format +msgid "Expected an integer \"%(key)s\" before \"%(token)s\"" +msgstr "Erwartete eine Ganzzahl \"%(key)s\" vor \"%(token)s\"" + +#, python-format +msgid "Expected an integer \"%(arg)s\" after \"%(key)s\"" +msgstr "Erwartete eine Ganzzahl \"%(arg)s\" nach \"%(key)s\"" + +#, python-format +msgid "Expected a color value \"%(arg)s\" after \"%(key)s\"" +msgstr "Erwartete einen Farbwert \"%(arg)s\" nach \"%(key)s\"" + +msgid "" +"Rendering of reStructured text is not possible, please install Docutils." +msgstr "" +"Anzeigen von reStructured Text ist nicht möglich, bitte installieren Sie " +"Docutils." + +msgid "**Maximum number of allowed includes exceeded**" +msgstr "**Maximale Anzahl erlaubter Includes überschritten**" + +#, python-format +msgid "**Could not find the referenced page: %s**" +msgstr "**Konnte die referenzierte Seite nicht finden: %s**" + +#, python-format +msgid "Inlined image: %(url)s" +msgstr "Eingebettetes Bild: %(url)s" + +#, python-format +msgid "Create new drawing \"%(filename)s (opens in new window)\"" +msgstr "Neue Zeichnung \"%(filename)s\" anlegen (öffnet ein neues Fenster)" + +#, python-format +msgid "Edit drawing %(filename)s (opens in new window)" +msgstr "Zeichnung %(filename)s bearbeiten (öffnet ein neues Fenster)" + +#, python-format +msgid "Clickable drawing: %(filename)s" +msgstr "Anklickbare Zeichnung %(filename)s" + +msgid "Toggle line numbers" +msgstr "Zeilennummern ein/ausschalten" + +msgid "[all]" +msgstr "[alle]" + +msgid "[not empty]" +msgstr "[nicht leer]" + +msgid "[empty]" +msgstr "[leer]" + +msgid "filter" +msgstr "Filter" + +msgid "Line" +msgstr "Zeile" + +msgid "No differences found!" +msgstr "Es wurden keine Änderungen gefunden!" + +msgid "Deletions are marked like this." +msgstr "Gelöschter Text ist auf diese Art markiert." + +msgid "Additions are marked like this." +msgstr "Hinzugefügter Text ist auf diese Art markiert." + +#, python-format +msgid "" +"Sorry, can not save page because \"%(content)s\" is not allowed in this wiki." +msgstr "" +"Kann die Seite nicht speichern, weil der Inhalt \"%(content)s\" in diesem " +"Wiki nicht erlaubt ist." + +msgid "Page" +msgstr "Seite" + +msgid "User" +msgstr "Benutzer" + +msgid "Diffs" +msgstr "DifferenzAnzeige" + +msgid "Info" +msgstr "Info" + +msgid "Unsubscribe" +msgstr "Nicht abonnieren" + +msgid "Subscribe" +msgstr "Abonnieren" + +msgid "Raw" +msgstr "Rohform" + +msgid "XML" +msgstr "XML" + +msgid "Print" +msgstr "Druckansicht" + +msgid "View" +msgstr "Anzeigen" + +msgid "Home" +msgstr "Heim" + +msgid "Up" +msgstr "Hoch" + +msgid "[RSS]" +msgstr "[RSS]" + +msgid "[DELETED]" +msgstr "[GELÖSCHT]" + +msgid "[UPDATED]" +msgstr "[AKTUALISIERT]" + +msgid "[RENAMED]" +msgstr "[UMBENANNT]" + +msgid "[CONFLICT]" +msgstr "[KONFLIKT]" + +msgid "[NEW]" +msgstr "[NEU]" + +msgid "[DIFF]" +msgstr "[DIFF]" + +msgid "[BOTTOM]" +msgstr "[FUSS]" + +msgid "[TOP]" +msgstr "[KOPF]" + +msgid "Click to do a full-text search for this title" +msgstr "Hier klicken für eine Liste der Seiten, die auf diese verweisen" + +msgid "Preferences" +msgstr "Einstellungen" + +msgid "Logout" +msgstr "Abmelden" + +msgid "Clear message" +msgstr "Nachricht löschen" + +#, python-format +msgid "last edited %(time)s by %(editor)s" +msgstr "zuletzt geändert am %(time)s durch %(editor)s" + +#, python-format +msgid "last modified %(time)s" +msgstr "zuletzt geändert %(time)s" + +msgid "Search:" +msgstr "Suchen:" + +msgid "Text" +msgstr "Text" + +msgid "Titles" +msgstr "Titel" + +msgid "Search" +msgstr "Suche" + +msgid "More Actions:" +msgstr "Weitere Aktionen:" + +msgid "------------------------" +msgstr "------------------------" + +msgid "Raw Text" +msgstr "Rohform" + +msgid "Print View" +msgstr "Druckansicht" + +msgid "Delete Cache" +msgstr "Cache löschen" + +msgid "Rename Page" +msgstr "Seite umbenennen" + +msgid "Copy Page" +msgstr "Seite kopieren" + +msgid "Delete Page" +msgstr "Seite löschen" + +msgid "Like Pages" +msgstr "Ähnliche Seiten" + +msgid "Local Site Map" +msgstr "ÜbersichtsKarte" + +msgid "My Pages" +msgstr "Meine Seiten" + +msgid "Subscribe User" +msgstr "Abo für Benutzer" + +msgid "Remove Spam" +msgstr "Spam entfernen" + +msgid "Revert to this revision" +msgstr "Diese Revision restaurieren" + +msgid "Package Pages" +msgstr "Seiten paketieren" + +msgid "Render as Docbook" +msgstr "Docbook ausgeben" + +msgid "Sync Pages" +msgstr "Seiten synchronisieren" + +msgid "Do" +msgstr "Los!" + +msgid "Comments" +msgstr "Kommentare" + +msgid "Edit (Text)" +msgstr "Editieren (Text)" + +msgid "Edit (GUI)" +msgstr "Editieren (GUI)" + +msgid "Immutable Page" +msgstr "Geschützte Seite" + +msgid "Remove Link" +msgstr "Verweis entfernen" + +msgid "Add Link" +msgstr "Verweis hinzufügen" + +#, python-format +msgid "Show %s days." +msgstr "%s Tage anzeigen." + +msgid "Wiki Markup" +msgstr "Wiki Quelltext" + +msgid "DeleteCache" +msgstr "CacheLöschen" + +#, python-format +msgid "(cached %s)" +msgstr "(gecached %s)" + +msgid "Or try one of these actions:" +msgstr "Oder benutze eine dieser Aktionen:" + +msgid "FrontPage" +msgstr "StartSeite" + +msgid "RecentChanges" +msgstr "AktuelleÄnderungen" + +msgid "TitleIndex" +msgstr "TitelIndex" + +msgid "WordIndex" +msgstr "WortIndex" + +msgid "FindPage" +msgstr "SeiteFinden" + +msgid "SiteNavigation" +msgstr "WegWeiser" + +msgid "HelpContents" +msgstr "HilfeInhalt" + +msgid "HelpOnFormatting" +msgstr "HilfeZumFormatieren" + +msgid "UserPreferences" +msgstr "BenutzerEinstellungen" + +msgid "WikiLicense" +msgstr "WikiLizenz" + +msgid "MissingPage" +msgstr "FehlendeSeite" + +msgid "MissingHomePage" +msgstr "FehlendePersönlicheSeite" + +msgid "Mon" +msgstr "Mo" + +msgid "Tue" +msgstr "Di" + +msgid "Wed" +msgstr "Mi" + +msgid "Thu" +msgstr "Do" + +msgid "Fri" +msgstr "Fr" + +msgid "Sat" +msgstr "Sa" + +msgid "Sun" +msgstr "So" + +msgid "AttachFile" +msgstr "DateiAnhänge" + +msgid "DeletePage" +msgstr "SeiteLöschen" + +msgid "LikePages" +msgstr "ÄhnlicheSeiten" + +msgid "LocalSiteMap" +msgstr "ÜbersichtsKarte" + +msgid "RenamePage" +msgstr "SeiteUmbenennen" + +msgid "SpellCheck" +msgstr "RechtSchreibung" + +#, python-format +msgid "Unknown action %(action_name)s." +msgstr "Unbekannte Aktion %(action_name)s." + +#, python-format +msgid "You are not allowed to do %(action_name)s on this page." +msgstr "Sie dürfen die Aktion %(action_name)s auf dieser Seite nicht benutzen!" + +msgid "Login and try again." +msgstr "Melden Sie sich an und probieren Sie es noch einmal." + +msgid "Charts are not available!" +msgstr "Die Diagrammoption ist nicht verfügbar!" + +msgid "Page Size Distribution" +msgstr "Verteilung der Seitengrößen" + +msgid "page size upper bound [bytes]" +msgstr "Obere Grenze der Seitengröße [bytes]" + +msgid "# of pages of this size" +msgstr "Anzahl der Seiten in dieser Größenklasse" + +msgid "User agent" +msgstr "Browsertyp" + +msgid "Others" +msgstr "Sonstige" + +msgid "Distribution of User-Agent Types" +msgstr "Verteilung der Zugriffe auf Browsertypen" + +msgid "Views/day" +msgstr "Lesezugriffe/Tag" + +msgid "Edits/day" +msgstr "Schreibzugriffe/Tag" + +msgid "Page hits and edits" +msgstr "Seitenzugriffe und Änderungen" + +#, python-format +msgid "%(chart_title)s for %(filterpage)s" +msgstr "%(chart_title)s für %(filterpage)s" + +msgid "" +"green=view\n" +"red=edit" +msgstr "" +"grün=Anzeigen\n" +"rot=Änderungen" + +msgid "date" +msgstr "Datum" + +msgid "# of hits" +msgstr "Anzahl der Zugriffe" + +msgid "" +" Emphasis:: [[Verbatim('')]]''italics''[[Verbatim('')]]; [[Verbatim" +"(''')]]'''bold'''[[Verbatim(''')]]; [[Verbatim(''''')]]'''''bold " +"italics'''''[[Verbatim(''''')]]; [[Verbatim('')]]''mixed ''[[Verbatim" +"(''')]]'''''bold'''[[Verbatim(''')]] and italics''[[Verbatim('')]]; " +"[[Verbatim(----)]] horizontal rule.\n" +" Headings:: [[Verbatim(=)]] Title 1 [[Verbatim(=)]]; [[Verbatim(==)]] Title " +"2 [[Verbatim(==)]]; [[Verbatim(===)]] Title 3 [[Verbatim(===)]]; [[Verbatim" +"(====)]] Title 4 [[Verbatim(====)]]; [[Verbatim(=====)]] Title 5 [[Verbatim" +"(=====)]].\n" +" Lists:: space and one of: * bullets; 1., a., A., i., I. numbered items; 1." +"#n start numbering at n; space alone indents.\n" +" Links:: [[Verbatim(JoinCapitalizedWords)]]; [[Verbatim([\"brackets and " +"double quotes\"])]]; url; [url]; [url label].\n" +" Tables:: || cell text |||| cell text spanning 2 columns ||; no trailing " +"white space allowed after tables or titles.\n" +"\n" +"(!) For more help, see HelpOnEditing or SyntaxReference.\n" +msgstr "" +" Betonung:: [[Verbatim('')]]''kursiv''[[Verbatim('')]]; [[Verbatim" +"(''')]]'''fett'''[[Verbatim(''')]]; [[Verbatim(''''')]]'''''fett und " +"kursiv'''''[[Verbatim(''''')]]; [[Verbatim('')]]''gemischt ''[[Verbatim" +"(''')]]'''''fett'''[[Verbatim(''')]] und kursiv''[[Verbatim('')]]; [[Verbatim" +"(----)]] horizontaler Balken.\n" +" Überschriften:: [[Verbatim(=)]] Überschrift 1 [[Verbatim(=)]]; [[Verbatim" +"(==)]] Überschrift 2 [[Verbatim(==)]]; [[Verbatim(===)]] Ü 3 [[Verbatim" +"(===)]]; [[Verbatim(====)]] Ü 4 [[Verbatim(====)]]; [[Verbatim(=====)]] Ü " +"5 [[Verbatim(=====)]].\n" +" Listen:: Leerzeichen und eins von: * Punkte; 1., a., A., i., I. nummerierte " +"Punkte; 1.#n starte Nummerierung bei n; nur Leerzeichen rückt ein.\n" +" Links:: [[Verbatim(ZusammenGeschriebeneGroßeWorte)]]; [[Verbatim" +"([\"Klammern und doppelte Anführungszeichen\"])]]; url; [url]; [url " +"label].\n" +" Tabellen:: || Zellentext |||| Zellentext, 2 Zellen überspannend ||; " +"keine anhängenden Leerzeichen nach Überschriften oder Tabellen.\n" +"\n" +"(!) Weitere Hilfe finden Sie unter HilfeZumEditieren oder SyntaxReferenz.\n" + +msgid "" +"Emphasis: *italic* **bold** ``monospace``
\n" +"
\n"
+"Headings: Heading 1  Heading 2  Heading 3\n"
+"          =========  ---------  ~~~~~~~~~\n"
+"\n"
+"Horizontal rule: ---- \n"
+"Links: TrailingUnderscore_ `multi word with backticks`_ external_ \n"
+"\n"
+".. _external: http://external-site.net/foo/\n"
+"\n"
+"Lists: * bullets; 1., a. numbered items.\n"
+"
\n" +"
\n" +"(!) For more help, see the \n" +"\n" +"reStructuredText Quick Reference\n" +".\n" +msgstr "" +"Betonung: *kursiv* **fett** ``gleiche Zeichenbreite``
\n" +"
\n"
+"Überschriften: Überschrift 1  Überschrift 2  Überschrift 3\n"
+"               =============  -------------  ~~~~~~~~~~~~~\n"
+"\n"
+"Horizontale Linie: ---- \n"
+"Links: AngehängterUnterstrich_ `mehrere Worte mit Rückwärtsapostroph`_ "
+"extern_ \n"
+"\n"
+".. _extern: http://externe-seite.de/\n"
+"\n"
+"Listen: * Punkte; 1., a. nummerierte Punkte.\n"
+"
\n" +"
\n" +"(!) Für mehr Hilfe siehe die \n" +"\n" +"reStructuredText Quick Reference\n" +".\n" + +msgid "UnSubscribe" +msgstr "Nicht abonnieren" + +msgid "Publish my email (not my wiki homepage) in author info" +msgstr "" +"Veröffentliche meine E-Mail-Adresse (nicht meine Wiki-Homepage) in der " +"Autoren-Info" + +msgid "Open editor on double click" +msgstr "Editor per Doppelklick öffnen" + +msgid "After login, jump to last visited page" +msgstr "Nach dem Anmelden zur zuletzt besuchten Seite springen" + +msgid "Show comment sections" +msgstr "Kommentarabschnitte anzeigen" + +msgid "Show question mark for non-existing pagelinks" +msgstr "Verweise auf unbekannte Seiten mit Fragezeichen markieren" + +msgid "Show page trail" +msgstr "Kürzlich besuchte Seiten anzeigen (Verlauf)" + +msgid "Show icon toolbar" +msgstr "Werkzeugleiste mit Bildsymbolen anzeigen" + +msgid "Show top/bottom links in headings" +msgstr "Verweise zum Anfang und Ende der Seite in Überschriften anzeigen" + +msgid "Show fancy diffs" +msgstr "Unterschiede farbig markiert anzeigen" + +msgid "Add spaces to displayed wiki names" +msgstr "Angezeigte Wikinamen mit Leerzeichen trennen" + +msgid "Remember login information" +msgstr "Speichere Login-Informationen" + +msgid "Subscribe to trivial changes" +msgstr "Triviale Änderungen abonnieren" + +msgid "Disable this account forever" +msgstr "Dieses Benutzerkonto für immer deaktivieren" + +msgid "(Use Firstname''''''Lastname)" +msgstr "(Vorname''''''Nachname verwenden)" + +msgid "Alias-Name" +msgstr "Alias-Name" + +msgid "Password repeat" +msgstr "Passwort wiederholen" + +msgid "(Only for password change or new account)" +msgstr "(Nur für Passwort-Änderung oder neue Benutzerkonten)" + +msgid "User CSS URL" +msgstr "Benutzer CSS URL" + +msgid "(Leave it empty for disabling user CSS)" +msgstr "Leer lassen, um benutzerdefiniertes CSS auszuschalten)" + +msgid "Editor size" +msgstr "Größe des Texteingabefelds" + +msgid "Do it." +msgstr "Ausführen" + +#, python-format +msgid "Execute action %(actionname)s?" +msgstr "Aktion %(actionname)s ausführen?" + +#, python-format +msgid "Action %(actionname)s is excluded in this wiki!" +msgstr "Aktion %(actionname)s ist ausgeschlossen in diesem Wiki!" + +#, python-format +msgid "You are not allowed to use action %(actionname)s on this page!" +msgstr "Sie dürfen die Aktion %(actionname)s auf dieser Seite nicht benutzen!" + +#, python-format +msgid "Please use the interactive user interface to use action %(actionname)s!" +msgstr "" +"Für die Aktion %(actionname)s bitte nur die vorgesehenen Webseiten benutzen!" + +msgid "You must login to add a quicklink." +msgstr "Sie müssen sich anmelden, um einen Expressverweis hinzuzufügen." + +msgid "Your quicklink to this page has been removed." +msgstr "Ihr Expressverweis für diese Seite wurde entfernt." + +msgid "Your quicklink to this page could not be removed." +msgstr "Ihr Expressverweis für diese Seite konnte nicht entfernt werden." + +msgid "A quicklink to this page has been added for you." +msgstr "Ein Expressverweis für diese Seite wurde hinzugefügt." + +msgid "A quicklink to this page could not be added for you." +msgstr "Ein Expressverweis für diese Seite konnte nicht hinzugefügt werden." + +msgid "Missing password. Please enter user name and password." +msgstr "Fehlendes Passwort. Bitte geben Sie Benutzername und Passwort ein." + +msgid "Sorry, login failed." +msgstr "Login fehlgeschlagen." + +#, python-format +msgid "[%d attachments]" +msgstr "[%d Anhänge]" + +#, python-format +msgid "" +"There are %(count)s attachment(s) stored for this " +"page." +msgstr "" +"Es sind %(count)s Anhänge für diese Seite " +"gespeichert." + +#, python-format +msgid "Attachment '%(target)s' already exists." +msgstr "Dateianhang '%(target)s' existiert bereits." + +msgid "Filename of attachment not specified!" +msgstr "Dateiname des Anhangs fehlt oder ist leer!" + +#, python-format +msgid "Attachment '%(filename)s' does not exist!" +msgstr "Dateianhang '%(filename)s' existiert nicht!" + +msgid "" +"To refer to attachments on a page, use '''{{{attachment:filename}}}''', \n" +"as shown below in the list of files. \n" +"Do '''NOT''' use the URL of the {{{[get]}}} link, \n" +"since this is subject to change and can break easily." +msgstr "" +"Um Dateianhänge in eine Seite einzufügen sollte unbedingt eine Angabe \n" +"wie '''{{{attachment:dateiname}}}''' benutzt werden, \n" +"wie sie auch in der folgenden Liste der Dateien erscheint. \n" +"Es sollte '''niemals''' die URL des Verweises (\"laden\") kopiert werden, \n" +"da sich diese jederzeit ändern kann und damit der Verweis auf die Datei " +"brechen würde." + +msgid "del" +msgstr "löschen" + +msgid "move" +msgstr "verschieben" + +msgid "get" +msgstr "laden" + +msgid "view" +msgstr "anzeigen" + +msgid "unzip" +msgstr "auspacken" + +msgid "install" +msgstr "installieren" + +#, python-format +msgid "No attachments stored for %(pagename)s" +msgstr "Es wurden keine Anhänge für die Seite %(pagename)s gespeichert." + +msgid "Edit drawing" +msgstr "Zeichnung editieren" + +msgid "New Attachment" +msgstr "Neuer Dateianhang" + +msgid "" +"An upload will never overwrite an existing file. If there is a name\n" +"conflict, you have to rename the file that you want to upload.\n" +"Otherwise, if \"Rename to\" is left blank, the original filename will be " +"used." +msgstr "" +"Ein neuer Anhang überschreibt niemals einen bereits vorhandenen gleichen " +"Namens.\n" +"Besteht ein Namenskonflikt, muss dem neuen Anhang ein alternativer Name " +"zugewiesen werden.\n" +"Ansonsten kann das Feld \"Umbenennen auf\" leer bleiben und es wird der " +"originale Dateiname benutzt." + +msgid "File to upload" +msgstr "Neuer Dateianhang" + +msgid "Rename to" +msgstr "Umbenennen auf" + +msgid "Overwrite existing attachment of same name" +msgstr "Anhänge gleichen Namens überschreiben" + +msgid "Upload" +msgstr "Datei hochladen" + +msgid "Attached Files" +msgstr "Gespeicherte Dateianhänge" + +msgid "You are not allowed to attach a file to this page." +msgstr "Sie dürfen keine Anhänge an diese Seite anhängen!" + +msgid "File attachments are not allowed in this wiki!" +msgstr "Dateianhänge sind in diesem Wiki nicht erlaubt!" + +msgid "You are not allowed to save a drawing on this page." +msgstr "Sie dürfen auf dieser Seite keine Zeichnung speichern." + +msgid "" +"No file content. Delete non ASCII characters from the file name and try " +"again." +msgstr "" +"Kein Dateiinhalt. Löschen Sie nicht-ASCII-Zeichen aus dem Dateinamen und " +"probieren Sie es noch einmal." + +msgid "You are not allowed to delete attachments on this page." +msgstr "Sie dürfen keine Anhänge dieser Seite löschen!" + +msgid "You are not allowed to move attachments from this page." +msgstr "Sie dürfen keine Anhänge von dieser Seite verschieben." + +msgid "Move aborted!" +msgstr "Verschieben abgebrochen!" + +msgid "Please use the interactive user interface to move attachments!" +msgstr "" +"Für die das Verschieben von Anhängen bitte nur die vorgesehenen Webseiten " +"benutzen!" + +msgid "You are not allowed to get attachments from this page." +msgstr "Sie dürfen auf keine Anhänge dieser Seite zugreifen." + +msgid "You are not allowed to unzip attachments of this page." +msgstr "Sie dürfen keine Anhänge dieser Seite auspacken." + +msgid "You are not allowed to install files." +msgstr "Sie dürfen keine Dateien installieren." + +msgid "You are not allowed to view attachments of this page." +msgstr "Sie dürfen keine Anhänge dieser Seite ansehen." + +#, python-format +msgid "Unsupported upload action: %s" +msgstr "Unbekannte Aktion für Dateianhang: %s" + +#, python-format +msgid "Attachments for \"%(pagename)s\"" +msgstr "Dateianhänge für \"%(pagename)s\"" + +#, python-format +msgid "" +"Attachment '%(target)s' (remote name '%(filename)s') with %(bytes)d bytes " +"saved." +msgstr "" +"Dateianhang '%(target)s' (ursprünglicher Name '%(filename)s') mit %(bytes)d " +"Bytes gesichert." + +#, python-format +msgid "Attachment '%(target)s' (remote name '%(filename)s') already exists." +msgstr "" +"Dateianhang '%(target)s' (ursprünglicher Name '%(filename)s') existiert " +"bereits." + +#, python-format +msgid "Attachment '%(filename)s' already exists." +msgstr "Dateianhang '%(filename)s' existiert bereits." + +#, python-format +msgid "Attachment '%(filename)s' moved to %(page)s." +msgstr "Dateianhang '%(filename)s' auf Seite %(page)s verschoben." + +msgid "Nothing changed" +msgstr "Keine Änderung." + +#, python-format +msgid "Page %(newpagename)s does not exists or you don't have enough rights." +msgstr "" +"Seite %(newpagename)s existiert nicht oder Sie haben nicht ausreichend " +"Rechte." + +msgid "Move aborted because empty page name" +msgstr "Sie können eine Seite nicht auf einen leeren Seitennamen umbenennen." + +#, python-format +msgid "Please use a valid filename for attachment '%(filename)s'." +msgstr "" +"Bitte benutzen Sie einen gültigen Dateinamen für Dateianhang '%(filename)s'." + +msgid "Move aborted because empty attachment name" +msgstr "Verschieben wegen eines leeren Anhangsnamens abgebrochen" + +msgid "Move" +msgstr "Verschieben" + +msgid "New page name" +msgstr "Neuer Seitenname" + +msgid "New attachment name" +msgstr "Neuer Name des Dateianhangs" + +#, python-format +msgid "Attachment '%(filename)s' installed." +msgstr "Dateianhang '%(filename)s' wurde installiert." + +#, python-format +msgid "" +"Attachment '%(filename)s' could not be unzipped because the resulting files " +"would be too large (%(space)d kB missing)." +msgstr "" +"Dateianhang '%(filename)s' konnte nicht ausgepackt werden, weil die " +"ausgepackten Dateien zu groß wären (%(space)d kB fehlen)." + +#, python-format +msgid "" +"Attachment '%(filename)s' could not be unzipped because the resulting files " +"would be too many (%(count)d missing)." +msgstr "" +"Dateianhang '%(filename)s' konnte nicht ausgepackt werden, weil die " +"ausgepackten Dateien zu viele wären (%(count)d fehlen)." + +#, python-format +msgid "Attachment '%(filename)s' unzipped." +msgstr "Dateianhang '%(filename)s' wurde ausgepackt." + +#, python-format +msgid "" +"Attachment '%(filename)s' not unzipped because the files are too big, .zip " +"files only, exist already or reside in folders." +msgstr "" +"Dateianhang '%(filename)s' wurde nicht ausgepackt, weil die Datei zu groß " +"sind, weil nur .zip-Dateien erlaubt sind, weil sie bereits existieren oder " +"weil Dateien in Ordnern enthalten sind." + +#, python-format +msgid "The file %(filename)s is not a .zip file." +msgstr "Die Datei %(filename)s ist keine .zip-Datei." + +#, python-format +msgid "Attachment '%(filename)s'" +msgstr "Dateianhang '%(filename)s'" + +msgid "Package script:" +msgstr "Paket-Skript:" + +msgid "File Name" +msgstr "Dateiname" + +msgid "Modified" +msgstr "Modifiziert" + +msgid "Size" +msgstr "Größe" + +msgid "Unknown file type, cannot display this attachment inline." +msgstr "" +"Dieser Anhang besitzt einen unbekannten Dateityp und kann deshalb nicht " +"direkt angezeigt werden." + +#, python-format +msgid "attachment:%(filename)s of %(pagename)s" +msgstr "[[Verbatim(attachment:)]]%(filename)s für %(pagename)s" + +msgid "This page is already deleted or was never created!" +msgstr "Diese Seite wurde bereits gelöscht oder wurde bisher nicht angelegt!" + +msgid "Rename all /subpages too?" +msgstr "Alle /UnterSeiten auch umbenennen?" + +msgid "New name" +msgstr "Neuer Name" + +msgid "Optional reason for the renaming" +msgstr "Optionale Begründung für das Umbenennen" + +msgid "Really rename this page?" +msgstr "Diese Seite wirklich umbenennen?" + +#, python-format +msgid "Full Link List for \"%s\"" +msgstr "Liste aller Seitenverweise für \"%s\"" + +msgid "Editor" +msgstr "Autor" + +msgid "Pages" +msgstr "Seiten" + +msgid "Select Author" +msgstr "Autor auswählen" + +msgid "Revert all!" +msgstr "Alle restaurieren!" + +msgid "You are not allowed to use this action." +msgstr "Sie dürfen diese Aktion nicht ausführen." + +#, python-format +msgid "Rolled back changes to the page %s." +msgstr "Änderungen an der Seite %s rückgängig gemacht" + +msgid "Exception while calling rollback function:" +msgstr "Fehler beim Aufrufen der Rollback-Funktion:" + +msgid "" +"Please enter your password of your account at the remote wiki below. " +"[[BR]] /!\\ You should trust both wikis because the password could be read " +"by the particular administrators." +msgstr "" +"Bitte geben Sie das Passwort Ihres Accounts im fernen Wiki unten ein. " +"[[BR]] /!\\ Sie sollten beiden Wikis vertrauen, weil das Passwort von den " +"entsprechenden Administratoren gelesen werden könnte." + +msgid "Operation was canceled." +msgstr "Operation wurde abgebrochen." + +msgid "The only supported directions are BOTH and DOWN." +msgstr "Es werden nur die Richtungen BOTH und DOWN unterstützt." + +msgid "" +"Please set an interwikiname in your wikiconfig (see HelpOnConfiguration) to " +"be able to use this action." +msgstr "" +"Bitte setzen Sie interwikiname in Ihrer wikiconfig (siehe " +"HilfeZurKonfiguration), um diese Aktion benutzen zu können." + +msgid "" +"Incorrect parameters. Please supply at least the ''remoteWiki'' parameter. " +"Refer to HelpOnSynchronisation for help." +msgstr "" +"Ungültige Parameter, bitte geben Sie mindestens den ''remoteWiki''-Parameter " +"an. Siehe HilfeZurSynchronisation für weitere Informationen." + +msgid "The ''remoteWiki'' is unknown." +msgstr "Das ''remoteWiki'' ist nicht bekannt." + +msgid "A severe error occured:" +msgstr "Ein schwerwiegender Fehler ist aufgetreten:" + +msgid "Synchronisation finished. Look below for the status messages." +msgstr "Synchronisierung beendet, siehe Status-Nachrichten unten." + +msgid "Synchronisation started -" +msgstr "Synchronisierung gestartet -" + +#, python-format +msgid "" +"Got a list of %s local and %s remote pages. This results in %s different " +"pages over-all." +msgstr "" +"%s lokale und %s ferne Seiten, resultierend in insgesamt %s " +"unterschiedlichen Seiten." + +#, python-format +msgid "After filtering: %s pages" +msgstr "Nach dem Filtern: %s Seiten" + +#, python-format +msgid "Skipped page %s because of no write access to local page." +msgstr "" +"Seite %s wurde wegen fehlenden Schreibrechten auf die lokale Seite " +"übersprungen." + +#, python-format +msgid "Deleted page %s locally." +msgstr "Lokale Seite %s gelöscht." + +#, python-format +msgid "Error while deleting page %s locally:" +msgstr "Fehler beim lokalen Löschen der Seite %s:" + +#, python-format +msgid "Deleted page %s remotely." +msgstr "Ferne Seite %s gelöscht." + +#, python-format +msgid "Error while deleting page %s remotely:" +msgstr "Fehler beim fernen Löschen der Seite %s:" + +#, python-format +msgid "" +"The item %s cannot be merged automatically but was changed in both wikis. " +"Please delete it in one of both wikis and try again." +msgstr "" +"Das Objekt %s kann nicht automatisch zusammengeführt werden, wurde aber in " +"beiden Wikis geändert. Bitte löschen Sie es in einem der beiden Wikis und " +"versuchen Sie es erneut." + +#, python-format +msgid "" +"The item %s has different mime types in both wikis and cannot be merged. " +"Please delete it in one of both wikis or unify the mime type, and try again." +msgstr "" +"Das Objekt %s hat einen unterschiedlichen Mime-Typ in beiden Wikis und kann " +"nicht zusammengeführt werden. Bitte löschen Sie es in einem der beiden Wikis " +"oder vereinheitlichen Sie den Mime-Typ und probieren Sie es nochmal." + +#, python-format +msgid "" +"The item %s was renamed locally. This is not implemented yet. Therefore the " +"full synchronisation history is lost for this page." +msgstr "" +"Seite %s wurde lokal umbenannt. Dies wird noch nicht unterstützt, daher geht " +"für diese Seite die ganze Synchronisierungs-Historie verloren." + +#, python-format +msgid "Synchronising page %s with remote page %s ..." +msgstr "Synchronisiere Seite %s mit der entfernten Seite %s ..." + +#, python-format +msgid "The page %s was deleted remotely but changed locally." +msgstr "Seite %s wurde lokal geändert, aber ferne gelöscht." + +#, python-format +msgid "" +"The page %s could not be synced. The remote page was renamed. This is not " +"supported yet. You may want to delete one of the pages to get it synced." +msgstr "" +"Seite %s konnte nicht synchronisiert werden. Die entfernte Seite wurde " +"umbenannt, was bis jetzt noch nicht unterstützt wird. Vielleicht möchten Sie " +"eine der Seiten löschen, um die Seite erfolgreich zu synchronisieren." + +#, python-format +msgid "Skipped page %s because of a locally or remotely unresolved conflict." +msgstr "" +"Seite %s wurde wegen eines lokalen oder entfernten nicht beseitigten " +"Konflikts übersprungen." + +#, python-format +msgid "" +"This is the first synchronisation between the local and the remote wiki for " +"the page %s." +msgstr "" +"Dies ist die erste Synchronisation zwischen dem lokalen und fernen Wiki für " +"die Seite %s." + +#, python-format +msgid "" +"The page %s could not be merged because you are not allowed to modify the " +"page in the remote wiki." +msgstr "" +"Die Seite %s konnte nicht zusammengeführt werden, weil Sie die Seite im " +"fernen Wiki nicht ändern dürfen." + +#, python-format +msgid "Page %s successfully merged." +msgstr "Seite \"%s\" wurde erfolgreich zusammengeführt." + +#, python-format +msgid "Page %s contains conflicts that were introduced on the remote side." +msgstr "Seite %s enthält von der fernen Seite eingeführte Konflikte." + +#, python-format +msgid "Page %s merged with conflicts." +msgstr "Seite %s wurde mit Konflikten zusammengeführt." + +msgid "Load" +msgstr "Laden" + +msgid "New Page or New Attachment" +msgstr "Neue Seite oder neuer Dateianhang" + +msgid "" +"You can upload a file to a new page or choose to upload a file as attachment " +"for the current page" +msgstr "" +"Sie können eine Datei in eine neue Seite hochladen oder eine Datei als " +"Dateianhang an die aktuelle Seite hochladen" + +msgid "attachment" +msgstr "Dateianhang" + +msgid "overwrite" +msgstr "überschreiben" + +msgid "New Name" +msgstr "Neuer Name" + +#, python-format +msgid "(including %(localwords)d %(pagelink)s)" +msgstr "(inklusive %(localwords)d %(pagelink)s)" + +#, python-format +msgid "" +"The following %(badwords)d words could not be found in the dictionary of %" +"(totalwords)d words%(localwords)s and are highlighted below:" +msgstr "" +"Die nachfolgenden %(badwords)d Worte konnten nicht im Wörterbuch mit %" +"(totalwords)d Worten%(localwords)s gefunden werden und sind im Text " +"hervorgehoben:" + +msgid "Add checked words to dictionary" +msgstr "Markierte Wörter zum Wörterbuch hinzufügen" + +msgid "No spelling errors found!" +msgstr "Keine Rechtschreibfehler gefunden!" + +msgid "You can't save spelling words." +msgstr "Sie können keine Rechtschreibkorrektur-Wörter abspeichern." + +msgid "You can't check spelling on a page you can't read." +msgstr "" +"Sie dürfen keine Seite auf Rechtschreibung prüfen, die Sie nicht lesen " +"können." + +msgid "You are now logged out." +msgstr "Sie sind nun abgemeldet." + +msgid "You are not allowed to subscribe to a page you can't read." +msgstr "Sie dürfen keine Seiten abonnieren, die Sie nicht lesen dürfen." + +msgid "This wiki is not enabled for mail processing." +msgstr "In diesem Wiki ist Mail-Verarbeitung nicht eingeschaltet." + +msgid "You must log in to use subscriptions." +msgstr "Sie müssen sich anmelden, um Abonnements verwenden zu können." + +msgid "Add your email address in your UserPreferences to use subscriptions." +msgstr "" +"Fügen Sie Ihre E-Mail-Adresse in den BenutzerEinstellungen hinzu, um " +"Abonnements benutzen zu können." + +msgid "Your subscription to this page has been removed." +msgstr "Ihr Abonnementsfür diese Seite wurde entfernt." + +msgid "Can't remove regular expression subscription!" +msgstr "Kann nicht Abonnement mit regulärem Ausdruck entfernen." + +msgid "Edit the subscription regular expressions in your UserPreferences." +msgstr "" +"Editieren Sie die regulären Ausdrücke für Abonnements in Ihren " +"BenutzerEinstellungen." + +msgid "You have been subscribed to this page." +msgstr "Die Seite wurde zur Liste abonnierter Seiten hinzugefügt." + +msgid "You could not get subscribed to this page." +msgstr "" +"Die Seite konnte nicht zur Liste abonnierter Seiten hinzugefügt werden." + +msgid "General Information" +msgstr "Allgemeine Informationen" + +#, python-format +msgid "Page size: %d" +msgstr "Seitengröße: %d" + +msgid "SHA digest of this page's content is:" +msgstr "Signatur des Seiteninhalts nach dem SHA-Verfahren:" + +msgid "The following users subscribed to this page:" +msgstr "Nachfolgende Benutzer haben diese Seite abonniert:" + +msgid "This page links to the following pages:" +msgstr "Diese Seite verweist auf die folgenden Seiten:" + +msgid "Diff" +msgstr "Differenz" + +msgid "Comment" +msgstr "Kommentar" + +msgid "Revision History" +msgstr "Versionshistorie" + +msgid "No log entries found." +msgstr "Keine Log-Einträge gefunden." + +#, python-format +msgid "Info for \"%s\"" +msgstr "Info für \"%s\"" + +#, python-format +msgid "Show \"%(title)s\"" +msgstr "\"%(title)s\" anzeigen" + +msgid "General Page Infos" +msgstr "Allgemeine Seiten-Informationen" + +msgid "Please log in first." +msgstr "Bitte melden Sie sich vorher an." + +msgid "Please first create a homepage before creating additional pages." +msgstr "" +"Bitte erzeugen Sie zuerst eine Homepage, bevor Sie weitere Seiten anlegen." + +#, python-format +msgid "" +"You can add some additional sub pages to your already existing homepage " +"here.\n" +"\n" +"You can choose how open to other readers or writers those pages shall be,\n" +"access is controlled by group membership of the corresponding group page.\n" +"\n" +"Just enter the sub page's name and click on the button to create a new " +"page.\n" +"\n" +"Before creating access protected pages, make sure the corresponding group " +"page\n" +"exists and has the appropriate members in it. Use HomepageGroupsTemplate for " +"creating\n" +"the group pages.\n" +"\n" +"||'''Add a new personal page:'''||'''Related access control list " +"group:'''||\n" +"||[[NewPage(HomepageReadWritePageTemplate,read-write page,%(username)s)]]||" +"[\"%(username)s/ReadWriteGroup\"]||\n" +"||[[NewPage(HomepageReadPageTemplate,read-only page,%(username)s)]]||[\"%" +"(username)s/ReadGroup\"]||\n" +"||[[NewPage(HomepagePrivatePageTemplate,private page,%(username)s)]]||%" +"(username)s only||\n" +"\n" +msgstr "" +"Hier können Sie zusätzliche Unterseiten zu Ihrer bereits existierenden " +"Homepage hinzufügen.\n" +"\n" +"Sie können wählen, wie offen diese Seiten für andere Leser oder Autoren sein " +"sollen,\n" +"der Zugriff wird über Gruppenmitgliedschaft in der entsprechenden Gruppe " +"kontrolliert.\n" +"\n" +"Geben Sie einfach den Namen der Unterseite ein und klicken Sie auf den " +"Knopf, um eine neue Seite zu erzeugen.\n" +"\n" +"Bevor Sie zugriffsgeschützte Seiten erzeugen, stellen Sie sicher, dass die " +"entsprechende Gruppenseite existiert und die richtigen Mitglieder hat. " +"Benutzen Sie HomepageGroupsTemplate für das Erzeugen der Gruppenseiten.\n" +"\n" +"||'''Neue persönliche Seite hinzufügen:'''||'''Zugeordnete ACL-Gruppe:'''||\n" +"||[[NewPage(HomepageReadWritePageTemplate,Seite (read/write),%(username)" +"s)]]||[\"%(username)s/ReadWriteGroup\"]||\n" +"||[[NewPage(HomepageReadPageTemplate,Seite (read-only),%(username)s)]]||[\"%" +"(username)s/ReadGroup\"]||\n" +"||[[NewPage(HomepagePrivatePageTemplate,Seite (privat),%(username)s)]]||nur %" +"(username)s||\n" +"\n" + +msgid "MyPages management" +msgstr "Verwaltung meiner Seiten" + +#, python-format +msgid "Subscribe users to the page %s" +msgstr "Seite %s für Benutzer abonnieren" + +#, python-format +msgid "Subscribed for %s:" +msgstr "Abonnenten von %s:" + +msgid "Not a user:" +msgstr "Kein Benutzer:" + +msgid "You are not allowed to perform this action." +msgstr "Sie dürfen diese Aktion nicht ausführen." + +#, python-format +msgid "(!) Only pages changed since '''%s''' are being displayed!" +msgstr "(!) Nur Seiten, die seit '''%s''' geändert wurden, werden angezeigt!" + +msgid "" +"/!\\ The modification date you entered was not recognized and is therefore " +"not considered for the search results!" +msgstr "" +"/!\\ Das eingegebene Änderungsdatum wurde nicht erkannt und wird deshalb " +"nicht bei der Suche berücksichtigt." + +#, python-format +msgid "Title Search: \"%s\"" +msgstr "Titelsuche: \"%s\"" + +#, python-format +msgid "Advanced Search: \"%s\"" +msgstr "Erweiterte Suche: \"%s\"" + +#, python-format +msgid "Full Text Search: \"%s\"" +msgstr "Volltextsuche: \"%s\"" + +#, python-format +msgid "" +"Your search query {{{\"%s\"}}} is invalid. Please refer to HelpOnSearching " +"for more information." +msgstr "" +"Ihre Suchanfrage {{{\"%s\"}}} ist ungültig. Siehe HilfeZumSuchen für weitere " +"Informationen." + +#, python-format +msgid "" +"Your search query {{{\"%s\"}}} didn't return any results. Please change some " +"terms and refer to HelpOnSearching for more information.%s" +msgstr "" +"Ihre Suche nach {{{\"%s\"}}} hat keine Resultate ergeben. Bitte ändern Sie " +"einige Suchbegriffe und lesen Sie für weitere Informationen auf " +"HilfeZumSuchen nach. %s" + +msgid "(!) Consider performing a" +msgstr "(!) Erwägen Sie eine" + +msgid "full-text search with your search terms" +msgstr "Volltextsuche mit Ihren Suchbegriffen" + +msgid "" +"(!) You're performing a title search that might not include all related " +"results of your search query in this wiki. [[BR]]" +msgstr "" +"(!) Sie führen eine Titelsuche durch, die möglicherweise nicht alle " +"relevanten Ergebnisse Ihrer Sucheanfrage in diesem Wiki enthält. [[BR]]" + +msgid "Click here to perform a full-text search with your search terms!" +msgstr "Hier klicken für eine Volltextsuche mit diesen Suchbegriffen!" + +#, python-format +msgid "" +"Restored Backup: %(filename)s to target dir: %(targetdir)s.\n" +"Files: %(filecount)d, Directories: %(dircount)d" +msgstr "" +"Wiederhergestelltes Backup: %(filename)s nach Zielverzeichnis: %(targetdir)" +"s.\n" +"Dateien: %(filecount)d, Verzeichnisse: %(dircount)d" + +#, python-format +msgid "Restoring backup: %(filename)s to target dir: %(targetdir)s failed." +msgstr "" +"Wiederherstellen von Backup %(filename)s in das Zielverzeichnis %(targetdir)" +"s fehlgeschlagen." + +msgid "Wiki Backup / Restore" +msgstr "Wiki Sicherung / Wiederherstellung" + +msgid "" +"Some hints:\n" +" * To restore a backup:\n" +" * Restoring a backup will overwrite existing data, so be careful.\n" +" * Rename it to .tar. (remove the --date--time--UTC " +"stuff).\n" +" * Put the backup file into the backup_storage_dir (use scp, ftp, ...).\n" +" * Hit the [[GetText(Restore)]] button below.\n" +"\n" +" * To make a backup, just hit the [[GetText(Backup)]] button and save the " +"file\n" +" you get to a secure place.\n" +"\n" +"Please make sure your wiki configuration backup_* values are correct and " +"complete.\n" +"\n" +msgstr "" +"Hinweise:\n" +" * Um ein Backup wiederherzustellen:\n" +" * Das Wiederherstellen eines Backups wird bestehende Daten überschreiben, " +"also seien Sie vorsichtig.\n" +" * Benennen Sie es auf .tar. um (entfernen Sie --date--" +"time--UTC).\n" +" * Legen Sie die Backupdatei in das backup_storage_dir (mit scp, " +"ftp, ...).\n" +" * Drücken Sie unten auf [[GetText(Restore)]]-Knopf unten.\n" +"\n" +" * Um ein Backup zu erstellen, drücken Sie einfach auf den [[GetText" +"(Backup)]]-Knopf und sichern Sie die Datei,\n" +" die Sie erhalten an eine sichere Stelle.\n" +"\n" +"Bitte stellen Sie sicher, dass die backup_* Werte in Ihrer Wiki-" +"Konfiguration korrekt und vollständig sind.\n" + +msgid "Backup" +msgstr "Datensicherung" + +msgid "Restore" +msgstr "Datenwiederherstellung" + +msgid "You are not allowed to do remote backup." +msgstr "Sie dürfen kein Remote-Backup ausführen." + +#, python-format +msgid "Unknown backup subaction: %s." +msgstr "Unbekannte backup Unteraktion: %s." + +msgid "You are not allowed to revert this page!" +msgstr "Sie dürfen diese Seite nicht restaurieren!" + +msgid "" +"You were viewing the current revision of this page when you called the " +"revert action. If you want to revert to an older revision, first view that " +"older revision and then call revert to this (older) revision again." +msgstr "" +"Sie haben die aktuelle Revision dieser Seite angeschaut als Sie die " +"Restaurieren-Funktion aufgerufen haben. Wenn Sie eine ältere Revision " +"restaurieren wollen, betrachten Sie erst diese ältere Revision und rufen Sie " +"dann die Restaurieren-Funktion für diese ältere Revision erneut auf." + +#, python-format +msgid "Local Site Map for \"%s\"" +msgstr "Lokale Seitenverweise für \"%s\"" + +#, python-format +msgid "No pages like \"%s\"!" +msgstr "Keine Seite ähnlich wie \"%s\"!" + +#, python-format +msgid "Invalid filename \"%s\"!" +msgstr "Ungültiger Dateiname \"%s\"!" + +#, python-format +msgid "Created the package %s containing the pages %s." +msgstr "Paket %s, das die Seiten %s enthält wurde erzeugt." + +msgid "Package pages" +msgstr "Seiten paketieren" + +msgid "Package name" +msgstr "Paketname" + +msgid "List of page names - separated by a comma" +msgstr "Liste von Seitennamen - getrennt durch ein Komma" + +msgid "No older revisions available!" +msgstr "Es sind keine älteren Versionen dieser Seite verfügbar!" + +#, python-format +msgid "Diff for \"%s\"" +msgstr "Änderungen von \"%s\"" + +#, python-format +msgid "Differences between revisions %d and %d" +msgstr "Unterschiede zwischen den Revisionen %d und %d" + +#, python-format +msgid "(spanning %d versions)" +msgstr "(über %d Versionen hinweg)" + +#, python-format +msgid "The page was saved %(count)d times, though!" +msgstr "Die Seite wurde jedoch %(count)d mal gespeichert!" + +msgid "(ignoring whitespace)" +msgstr "(ignoriere Leerraum)" + +msgid "Ignore changes in the amount of whitespace" +msgstr "Ausschließlich Leerraum betreffende Änderungen ignorieren" + +#, python-format +msgid "Exactly one page like \"%s\" found, redirecting to page." +msgstr "Genau eine Seite wie \"%s\" gefunden, leite dorthin weiter." + +#, python-format +msgid "Pages like \"%s\"" +msgstr "Seiten ähnlich wie \"%s\"" + +#, python-format +msgid "%(matchcount)d %(matches)s for \"%(title)s\"" +msgstr "%(matchcount)d %(matches)s passen zu \"%(title)s\"" + +msgid "Copy all /subpages too?" +msgstr "Alle /UnterSeiten auch kopieren?" + +msgid "Optional reason for the copying" +msgstr "Optionale Begründung für das Kopieren" + +msgid "Really copy this page?" +msgstr "Diese Seite wirklich kopieren?" + +msgid "" +"Cannot create a new page without a page name. Please specify a page name." +msgstr "" +"Kann keine neue Seite ohne Seitennamen anlegen - bitte geben Sie einen " +"Seitennamen an." + +msgid "Delete" +msgstr "Löschen" + +msgid "Delete all /subpages too?" +msgstr "Alle /UnterSeiten auch löschen?" + +msgid "Optional reason for the deletion" +msgstr "Optionale Begründung für die Löschung" + +msgid "Really delete this page?" +msgstr "Diese Seite wirklich löschen?" + +#~ msgid "filename" +#~ msgstr "Dateiname" + +#~ msgid "" +#~ "~-If you submit this form, the submitted values will be displayed.\n" +#~ "To use this form on other pages, insert a\n" +#~ "[[BR]][[BR]]'''{{{ [[Form(\"%(pagename)s\")]]}}}'''[[BR]][[BR]]\n" +#~ "macro call.-~\n" +#~ msgstr "" +#~ "~-Das Absenden dieses Formulars zeigt die eingegebenen Werte an.\n" +#~ "Um das Formular auf anderen Seiten zu benutzen, muss folgender " +#~ "Makroaufruf\n" +#~ "[[BR]][[BR]]'''{{{ [[Form(\"%(pagename)s\")]]}}}'''[[BR]][[BR]]\n" +#~ "auf diesen Seiten platziert werden.-~\n" + +#~ msgid "" +#~ "Unknown user name: {{{\"%s\"}}}. Please enter user name and password." +#~ msgstr "" +#~ "Unbekannter Benutzername: {{{\"%s\"}}}. Bitte geben Sie Benutzername und " +#~ "Passwort ein." diff --git a/tests/examplefiles/django_sample.html+django b/tests/examplefiles/django_sample.html+django new file mode 100644 index 0000000..b1fdc5e --- /dev/null +++ b/tests/examplefiles/django_sample.html+django @@ -0,0 +1,68 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_modify adminmedia %} +{% block extrahead %}{{ block.super }} + +{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %} +{% endblock %} +{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %} +{% block coltype %}{% if ordered_objects %}colMS{% else %}colM{% endif %}{% endblock %} +{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %} +{% block userlinks %}{% trans 'Documentation' %} / {% trans 'Change password' %} / {% trans 'Log out' %}{% endblock %} +{% block breadcrumbs %}{% if not is_popup %} + +{% endif %}{% endblock %} +{% block content %}
+{% if change %}{% if not is_popup %} + +{% endif %}{% endif %} +
{% block form_top %}{% endblock %} +
+{% if is_popup %}{% endif %} +{% if opts.admin.save_on_top %}{% submit_row %}{% endif %} +{% if form.error_dict %} +

+ {% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %} +

+{% endif %} +{% for bound_field_set in bound_field_sets %} +
+ {% if bound_field_set.name %}

{{ bound_field_set.name }}

{% endif %} + {% if bound_field_set.description %}
{{ bound_field_set.description }}
{% endif %} + {% for bound_field_line in bound_field_set %} + {% admin_field_line bound_field_line %} + {% for bound_field in bound_field_line %} + {% filter_interface_script_maybe bound_field %} + {% endfor %} + {% endfor %} +
+{% endfor %} +{% block after_field_sets %}{% endblock %} +{% if change %} + {% if ordered_objects %} +

{% trans "Ordering" %}

+
+ {% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %} +

{{ form.order_ }}

+
+ {% endif %} +{% endif %} +{% for related_object in inline_related_objects %}{% edit_inline related_object %}{% endfor %} +{% block after_related_objects %}{% endblock %} +{% submit_row %} +{% if add %} + +{% endif %} +{% if auto_populated_fields %} + +{% endif %} +
+
+{% endblock %} diff --git a/tests/examplefiles/dwarf.cw b/tests/examplefiles/dwarf.cw new file mode 100644 index 0000000..ad8bb61 --- /dev/null +++ b/tests/examplefiles/dwarf.cw @@ -0,0 +1,17 @@ +;redcode +;name Dwarf +;author A. K. Dewdney +;version 94.1 +;date April 29, 1993 +;strategy Bombs every fourth instruction. + ORG start ; Indicates the instruction with + ; the label "start" should be the + ; first to execute. +step EQU 4 ; Replaces all occurrences of "step" + ; with the character "4". +target DAT.F #0, #0 ; Pointer to target instruction. +start ADD.AB #step, target ; Increments pointer by step. + MOV.AB #0, @target ; Bombs target instruction. + JMP.A start ; Same as JMP.A -2. Loops back to + ; the instruction labelled "start". + END diff --git a/tests/examplefiles/erl_session b/tests/examplefiles/erl_session new file mode 100644 index 0000000..c207781 --- /dev/null +++ b/tests/examplefiles/erl_session @@ -0,0 +1,10 @@ +1> io:format("Hello\n"). +Hello +ok +2> fun(X) -> X + 1 end. +#Fun +3> 123 + 234. +357 +4> X = Y. +* 1: variable 'Y' is unbound +5> diff --git a/tests/examplefiles/escape_semicolon.clj b/tests/examplefiles/escape_semicolon.clj new file mode 100644 index 0000000..b46a852 --- /dev/null +++ b/tests/examplefiles/escape_semicolon.clj @@ -0,0 +1 @@ +(= c (int \;)) (do (.readLine s) :line-start) diff --git a/tests/examplefiles/evil_regex.js b/tests/examplefiles/evil_regex.js new file mode 100644 index 0000000..3839c3f --- /dev/null +++ b/tests/examplefiles/evil_regex.js @@ -0,0 +1,48 @@ +/regexp/.test(foo) || x = [/regexp/,/regexp/, /regexp/, // comment +// comment +/regexp/]; +if (/regexp/.test(string)) +{/regexp/.test(string);}; +x =/regexp/; +x = /regexp/; +if (0/regexp/.exec(string)) +x = { u:/regexp/, v: /regexp/ }; +foo();/regexp/.test(string); /regexp/.test(string); +if (!/regexp/) foobar(); +x = u %/regexp/.exec(string) */regexp/.exec(string) / /regexp/.exec(string); +x = u?/regexp/.exec(string) : v +/regexp/.exec(string) -/regexp/.exec(string); +a = u^/regexp/.exec(string) &/regexp/.exec(string) |/regexp/.exec(string) +~/regexp/.exec(string); +x = /regexp/ /* a comment */ ; +x = /[reg/exp]/; +x = 4/2/i; +x = (a == b) ?/* this is a comment */ c : d; +/// a comment // +a = /regex//2/1; //syntactically correct, returns NaN + + + + +/* original examples */ + +// regex + +blah(/abc/); +x = /abc/; +x = /abc/.match; + +// math + +blah(1/2); //comment +x = 1 / 2 / 3; +x = 1/1/.1; + +// broken + +x=/1/; +x=1/a/g; +x=a/a/g; + +// real-world + +var x = 1/(1+Math.sqrt(sum)); // convert to number between 1-0 +return Math.round((num / den) * 100)/100; diff --git a/tests/examplefiles/example.aspx b/tests/examplefiles/example.aspx new file mode 100644 index 0000000..01de00e --- /dev/null +++ b/tests/examplefiles/example.aspx @@ -0,0 +1,27 @@ +<%@ Page Language="C#" %> + + + + + + + + Sample page + + +
+
+ The current time is: +
+
+ + + diff --git a/tests/examplefiles/example.c b/tests/examplefiles/example.c new file mode 100644 index 0000000..a7f546d --- /dev/null +++ b/tests/examplefiles/example.c @@ -0,0 +1,2080 @@ +#include +#include +#include +#include "codegen.h" +#include "symboltable.h" +#include "stringbuffer.h" + +extern void yyerror(char* msg); + +static stringBuffer* staticVariableBuffer; +static stringBuffer* classInitBuffer; +static stringBuffer* currentMethodBuffer; +static stringBuffer* finishedMethodsBuffer; +static stringBuffer* mainBuffer; + +static int currentMethodBufferIndex; +static int currentMethodStackSize; +static int currentMethodStackSizeMax; +static int currentMethodNumberOfLocals; + +static int classInitBufferIndex; +static int classInitStackSize; +static int classInitStackSizeMax; + +static int labelCounter = 0; +static int global = 1; + +char tempString[MAX_LENGTH_OF_COMMAND]; + +extern char* className; /* from minako-syntax.y */ + +/* forward declarations */ +static void increaseStackby(int stackdiff); +char convertType(int type); + +void codegenInit() { + staticVariableBuffer = newStringBuffer(); + classInitBuffer = newStringBuffer(); + currentMethodBuffer = 0; + finishedMethodsBuffer = newStringBuffer(); + mainBuffer = newStringBuffer(); + + stringBufferAppend(mainBuffer, "; ------- Header --------------------------------------------"); + sprintf(tempString, ".class public synchronized %s", className); + stringBufferAppend(mainBuffer, tempString); + stringBufferAppend(mainBuffer, ".super java/lang/Object"); + stringBufferAppend(mainBuffer, "; -----------------------------------------------------------"); + stringBufferAppend(mainBuffer, ""); + + stringBufferAppend(finishedMethodsBuffer, "; ------- Constructor ---------------------------------------"); + stringBufferAppend(finishedMethodsBuffer, ".method public ()V"); + stringBufferAppend(finishedMethodsBuffer, "\t.limit stack 1"); + stringBufferAppend(finishedMethodsBuffer, "\t.limit locals 1"); + stringBufferAppend(finishedMethodsBuffer, "\taload_0"); + stringBufferAppend(finishedMethodsBuffer, "\tinvokenonvirtual java/lang/Object/()V"); + stringBufferAppend(finishedMethodsBuffer, "\treturn"); + stringBufferAppend(finishedMethodsBuffer, ".end method"); + stringBufferAppend(finishedMethodsBuffer, "; -----------------------------------------------------------"); + stringBufferAppend(finishedMethodsBuffer, ""); + + stringBufferAppend(staticVariableBuffer, "; ------- Class Variables -----------------------------------"); + + stringBufferAppend(classInitBuffer, "; ------- Class Initializer ---------------------------------"); + stringBufferAppend(classInitBuffer, ".method static ()V"); + classInitBufferIndex = classInitBuffer->numberOfNextElement; + stringBufferAppend(classInitBuffer, "\t.limit locals 0"); + +} + +void codegenAppendCommand(char* cmd, int stackdiff) { + char tempString[MAX_LENGTH_OF_COMMAND]; + sprintf(tempString, "\t%s", cmd); + if (global) stringBufferAppend(classInitBuffer, tempString); + else stringBufferAppend(currentMethodBuffer, tempString); + increaseStackby(stackdiff); +} + +void codegenInsertCommand(int address, char* cmd, int stackdiff) { + char tempString[MAX_LENGTH_OF_COMMAND]; + sprintf(tempString, "\t%s", cmd); + if (global) stringBufferInsert(classInitBuffer, address, tempString); + else stringBufferInsert(currentMethodBuffer, address, tempString); + increaseStackby(stackdiff); +} + +void codegenAppendLabel(int label) { + char tempString[MAX_LENGTH_OF_COMMAND]; + sprintf(tempString, "Label%d:", label); + if (global) stringBufferAppend(classInitBuffer, tempString); + else stringBufferAppend(currentMethodBuffer, tempString); +} + +void codegenAddVariable(char* name, int type) { + /*fprintf(stderr, "add variable %s(%d) global=%d ", name, convertType(type), global);*/ + if (global) { + if (type == TYPE_INT) sprintf(tempString, ".field static %s %c", name, 'I'); + else if (type == TYPE_FLOAT) sprintf(tempString, ".field static %s %c", name, 'F'); + else if (type == TYPE_BOOLEAN) sprintf(tempString, ".field static %s %c", name, 'Z'); + else yyerror("compiler-intern error in codegenAddGlobalVariable().\n"); + stringBufferAppend(staticVariableBuffer, tempString); + } + else { + currentMethodNumberOfLocals++; + } +} + +int codegenGetNextLabel() { + return labelCounter++; +} + +int codegenGetCurrentAddress() { + if (global) return classInitBuffer->numberOfNextElement; + else return currentMethodBuffer->numberOfNextElement; +} + +void codegenEnterFunction(symtabEntry* entry) { + currentMethodBuffer = newStringBuffer(); + currentMethodStackSize = 0; + currentMethodStackSizeMax = 0; + labelCounter = 1; + global = 0; + + if (strcmp(entry->name, "main") == 0) { + if (entry->idtype != TYPE_VOID) yyerror("main has to be void.\n"); + currentMethodNumberOfLocals = 1; + symtabInsert(strdup("#main-param#"), TYPE_VOID, CLASS_FUNC); + stringBufferAppend(currentMethodBuffer, "; ------- Methode ---- void main() --------------------------"); + stringBufferAppend(currentMethodBuffer, ".method public static main([Ljava/lang/String;)V"); + } + else { + int i; + currentMethodNumberOfLocals = entry->paramIndex; + stringBufferAppend(currentMethodBuffer, "; ------- Methode -------------------------------------------"); + sprintf(tempString, ".method public static %s(", entry->name); + for (i=entry->paramIndex-1; i>=0; i--) { + int type = entry->params[i]->idtype; + tempString[strlen(tempString)+1] = 0; + tempString[strlen(tempString)] = convertType(type); + } + tempString[strlen(tempString)+2] = 0; + tempString[strlen(tempString)+1] = convertType(entry->idtype); + tempString[strlen(tempString)] = ')'; + stringBufferAppend(currentMethodBuffer, tempString); + } + currentMethodBufferIndex = currentMethodBuffer->numberOfNextElement; +} + +void codegenLeaveFunction() { + global = 1; + sprintf(tempString, "\t.limit locals %d", currentMethodNumberOfLocals); + stringBufferInsert(currentMethodBuffer, currentMethodBufferIndex, tempString); + sprintf(tempString, "\t.limit stack %d", currentMethodStackSizeMax); + stringBufferInsert(currentMethodBuffer, currentMethodBufferIndex, tempString); + stringBufferAppend(currentMethodBuffer, "\treturn"); + stringBufferAppend(currentMethodBuffer, ".end method"); + stringBufferAppend(currentMethodBuffer, "; -----------------------------------------------------------"); + stringBufferAppend(currentMethodBuffer, ""); + + stringBufferConcatenate(finishedMethodsBuffer, currentMethodBuffer); +} + + + +void codegenFinishCode() { + stringBufferAppend(staticVariableBuffer, "; -----------------------------------------------------------"); + stringBufferAppend(staticVariableBuffer, ""); + + sprintf(tempString, "\t.limit stack %d", classInitStackSizeMax); + stringBufferInsert(classInitBuffer, classInitBufferIndex, tempString); + stringBufferAppend(classInitBuffer, "\treturn"); + stringBufferAppend(classInitBuffer, ".end method"); + stringBufferAppend(classInitBuffer, "; -----------------------------------------------------------"); + + stringBufferConcatenate(mainBuffer, staticVariableBuffer); + stringBufferConcatenate(mainBuffer, finishedMethodsBuffer); + stringBufferConcatenate(mainBuffer, classInitBuffer); + + stringBufferPrint(mainBuffer); +} + +static void increaseStackby(int stackdiff) { + if (global) { + classInitStackSize += stackdiff; + if (classInitStackSize > classInitStackSizeMax) classInitStackSizeMax = classInitStackSize; + } + else { + currentMethodStackSize += stackdiff; + if (currentMethodStackSize > currentMethodStackSizeMax) currentMethodStackSizeMax = currentMethodStackSize; + } +} + +char convertType(int type) { + switch(type) { + case TYPE_VOID: return 'V'; + case TYPE_INT: return 'I'; + case TYPE_FLOAT: return 'F'; + case TYPE_BOOLEAN: return 'Z'; + default: yyerror("compiler-intern error in convertType().\n"); + } + return 0; /* to avoid compiler-warning */ +} + + +//#include +//#include + +int main() { + int a = 12, b = 44; + while (a != b) { + if (a > b) + a -= b; + else + b -= a; + } + printf("%d\n%d", a, 0X0);\ +} + + +/********************************************************************** + + array.c - + + $Author: murphy $ + $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $ + created at: Fri Aug 6 09:46:12 JST 1993 + + Copyright (C) 1993-2003 Yukihiro Matsumoto + Copyright (C) 2000 Network Applied Communication Laboratory, Inc. + Copyright (C) 2000 Information-technology Promotion Agency, Japan + +**********************************************************************/ + +#include "ruby.h" +#include "util.h" +#include "st.h" +#include "node.h" + +VALUE rb_cArray, rb_cValues; + +static ID id_cmp; + +#define ARY_DEFAULT_SIZE 16 + + +void +rb_mem_clear(mem, size) + register VALUE *mem; + register long size; +{ + while (size--) { + *mem++ = Qnil; + } +} + +static inline void +memfill(mem, size, val) + register VALUE *mem; + register long size; + register VALUE val; +{ + while (size--) { + *mem++ = val; + } +} + +#define ARY_TMPLOCK FL_USER1 + +static inline void +rb_ary_modify_check(ary) + VALUE ary; +{ + if (OBJ_FROZEN(ary)) rb_error_frozen("array"); + if (FL_TEST(ary, ARY_TMPLOCK)) + rb_raise(rb_eRuntimeError, "can't modify array during iteration"); + if (!OBJ_TAINTED(ary) && rb_safe_level() >= 4) + rb_raise(rb_eSecurityError, "Insecure: can't modify array"); +} + +static void +rb_ary_modify(ary) + VALUE ary; +{ + VALUE *ptr; + + rb_ary_modify_check(ary); + if (FL_TEST(ary, ELTS_SHARED)) { + ptr = ALLOC_N(VALUE, RARRAY(ary)->len); + FL_UNSET(ary, ELTS_SHARED); + RARRAY(ary)->aux.capa = RARRAY(ary)->len; + MEMCPY(ptr, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len); + RARRAY(ary)->ptr = ptr; + } +} + +VALUE +rb_ary_freeze(ary) + VALUE ary; +{ + return rb_obj_freeze(ary); +} + +/* + * call-seq: + * array.frozen? -> true or false + * + * Return true if this array is frozen (or temporarily frozen + * while being sorted). + */ + +static VALUE +rb_ary_frozen_p(ary) + VALUE ary; +{ + if (OBJ_FROZEN(ary)) return Qtrue; + if (FL_TEST(ary, ARY_TMPLOCK)) return Qtrue; + return Qfalse; +} + +static VALUE ary_alloc(VALUE); +static VALUE +ary_alloc(klass) + VALUE klass; +{ + NEWOBJ(ary, struct RArray); + OBJSETUP(ary, klass, T_ARRAY); + + ary->len = 0; + ary->ptr = 0; + ary->aux.capa = 0; + + return (VALUE)ary; +} + +static VALUE +ary_new(klass, len) + VALUE klass; + long len; +{ + VALUE ary; + + if (len < 0) { + rb_raise(rb_eArgError, "negative array size (or size too big)"); + } + if (len > 0 && len * sizeof(VALUE) <= len) { + rb_raise(rb_eArgError, "array size too big"); + } + if (len == 0) len++; + + ary = ary_alloc(klass); + RARRAY(ary)->ptr = ALLOC_N(VALUE, len); + RARRAY(ary)->aux.capa = len; + + return ary; +} + +VALUE +rb_ary_new2(len) + long len; +{ + return ary_new(rb_cArray, len); +} + + +VALUE +rb_ary_new() +{ + return rb_ary_new2(ARY_DEFAULT_SIZE); +} + +#ifdef HAVE_STDARG_PROTOTYPES +#include +#define va_init_list(a,b) va_start(a,b) +#else +#include +#define va_init_list(a,b) va_start(a) +#endif + +VALUE +#ifdef HAVE_STDARG_PROTOTYPES +rb_ary_new3(long n, ...) +#else +rb_ary_new3(n, va_alist) + long n; + va_dcl +#endif +{ + va_list ar; + VALUE ary; + long i; + + ary = rb_ary_new2(n); + + va_init_list(ar, n); + for (i=0; iptr[i] = va_arg(ar, VALUE); + } + va_end(ar); + + RARRAY(ary)->len = n; + return ary; +} + +VALUE +rb_ary_new4(n, elts) + long n; + const VALUE *elts; +{ + VALUE ary; + + ary = rb_ary_new2(n); + if (n > 0 && elts) { + MEMCPY(RARRAY(ary)->ptr, elts, VALUE, n); + } + RARRAY(ary)->len = n; + + return ary; +} + +VALUE +#ifdef HAVE_STDARG_PROTOTYPES +rb_values_new(long n, ...) +#else +rb_values_new(n, va_alist) + long n; + va_dcl +#endif +{ + va_list ar; + VALUE val; + long i; + + val = ary_new(rb_cValues, n); + va_init_list(ar, n); + for (i=0; iptr[i] = va_arg(ar, VALUE); + } + va_end(ar); + RARRAY(val)->len = n; + + return val; +} + +VALUE +rb_values_new2(n, elts) + long n; + const VALUE *elts; +{ + VALUE val; + + val = ary_new(rb_cValues, n); + if (n > 0 && elts) { + RARRAY(val)->len = n; + MEMCPY(RARRAY(val)->ptr, elts, VALUE, n); + } + + return val; +} + +static VALUE +ary_make_shared(ary) + VALUE ary; +{ + if (!FL_TEST(ary, ELTS_SHARED)) { + NEWOBJ(shared, struct RArray); + OBJSETUP(shared, rb_cArray, T_ARRAY); + + shared->len = RARRAY(ary)->len; + shared->ptr = RARRAY(ary)->ptr; + shared->aux.capa = RARRAY(ary)->aux.capa; + RARRAY(ary)->aux.shared = (VALUE)shared; + FL_SET(ary, ELTS_SHARED); + OBJ_FREEZE(shared); + return (VALUE)shared; + } + else { + return RARRAY(ary)->aux.shared; + } +} + +static VALUE +ary_shared_array(klass, ary) + VALUE klass, ary; +{ + VALUE val = ary_alloc(klass); + + ary_make_shared(ary); + RARRAY(val)->ptr = RARRAY(ary)->ptr; + RARRAY(val)->len = RARRAY(ary)->len; + RARRAY(val)->aux.shared = RARRAY(ary)->aux.shared; + FL_SET(val, ELTS_SHARED); + return val; +} + +VALUE +rb_values_from_ary(ary) + VALUE ary; +{ + return ary_shared_array(rb_cValues, ary); +} + +VALUE +rb_ary_from_values(val) + VALUE val; +{ + return ary_shared_array(rb_cArray, val); +} + +VALUE +rb_assoc_new(car, cdr) + VALUE car, cdr; +{ + return rb_values_new(2, car, cdr); +} + +static VALUE +to_ary(ary) + VALUE ary; +{ + return rb_convert_type(ary, T_ARRAY, "Array", "to_ary"); +} + +static VALUE +to_a(ary) + VALUE ary; +{ + return rb_convert_type(ary, T_ARRAY, "Array", "to_a"); +} + +VALUE +rb_check_array_type(ary) + VALUE ary; +{ + return rb_check_convert_type(ary, T_ARRAY, "Array", "to_ary"); +} + +static VALUE rb_ary_replace _((VALUE, VALUE)); + +/* + * call-seq: + * Array.new(size=0, obj=nil) + * Array.new(array) + * Array.new(size) {|index| block } + * + * Returns a new array. In the first form, the new array is + * empty. In the second it is created with _size_ copies of _obj_ + * (that is, _size_ references to the same + * _obj_). The third form creates a copy of the array + * passed as a parameter (the array is generated by calling + * to_ary on the parameter). In the last form, an array + * of the given size is created. Each element in this array is + * calculated by passing the element's index to the given block and + * storing the return value. + * + * Array.new + * Array.new(2) + * Array.new(5, "A") + * + * # only one copy of the object is created + * a = Array.new(2, Hash.new) + * a[0]['cat'] = 'feline' + * a + * a[1]['cat'] = 'Felix' + * a + * + * # here multiple copies are created + * a = Array.new(2) { Hash.new } + * a[0]['cat'] = 'feline' + * a + * + * squares = Array.new(5) {|i| i*i} + * squares + * + * copy = Array.new(squares) + */ + +static VALUE +rb_ary_initialize(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + long len; + VALUE size, val; + + if (rb_scan_args(argc, argv, "02", &size, &val) == 0) { + RARRAY(ary)->len = 0; + if (rb_block_given_p()) { + rb_warning("given block not used"); + } + return ary; + } + + if (argc == 1 && !FIXNUM_P(size)) { + val = rb_check_array_type(size); + if (!NIL_P(val)) { + rb_ary_replace(ary, val); + return ary; + } + } + + len = NUM2LONG(size); + if (len < 0) { + rb_raise(rb_eArgError, "negative array size"); + } + if (len > 0 && len * (long)sizeof(VALUE) <= len) { + rb_raise(rb_eArgError, "array size too big"); + } + rb_ary_modify(ary); + if (len > RARRAY(ary)->aux.capa) { + REALLOC_N(RARRAY(ary)->ptr, VALUE, len); + RARRAY(ary)->aux.capa = len; + } + if (rb_block_given_p()) { + long i; + + if (argc == 2) { + rb_warn("block supersedes default value argument"); + } + for (i=0; ilen = i + 1; + } + } + else { + memfill(RARRAY(ary)->ptr, len, val); + RARRAY(ary)->len = len; + } + + return ary; +} + + +/* +* Returns a new array populated with the given objects. +* +* Array.[]( 1, 'a', /^A/ ) +* Array[ 1, 'a', /^A/ ] +* [ 1, 'a', /^A/ ] +*/ + +static VALUE +rb_ary_s_create(argc, argv, klass) + int argc; + VALUE *argv; + VALUE klass; +{ + VALUE ary = ary_alloc(klass); + + if (argc > 0) { + RARRAY(ary)->ptr = ALLOC_N(VALUE, argc); + MEMCPY(RARRAY(ary)->ptr, argv, VALUE, argc); + } + RARRAY(ary)->len = RARRAY(ary)->aux.capa = argc; + + return ary; +} + +void +rb_ary_store(ary, idx, val) + VALUE ary; + long idx; + VALUE val; +{ + if (idx < 0) { + idx += RARRAY(ary)->len; + if (idx < 0) { + rb_raise(rb_eIndexError, "index %ld out of array", + idx - RARRAY(ary)->len); + } + } + + rb_ary_modify(ary); + if (idx >= RARRAY(ary)->aux.capa) { + long new_capa = RARRAY(ary)->aux.capa / 2; + + if (new_capa < ARY_DEFAULT_SIZE) { + new_capa = ARY_DEFAULT_SIZE; + } + new_capa += idx; + if (new_capa * (long)sizeof(VALUE) <= new_capa) { + rb_raise(rb_eArgError, "index too big"); + } + REALLOC_N(RARRAY(ary)->ptr, VALUE, new_capa); + RARRAY(ary)->aux.capa = new_capa; + } + if (idx > RARRAY(ary)->len) { + rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len, + idx-RARRAY(ary)->len + 1); + } + + if (idx >= RARRAY(ary)->len) { + RARRAY(ary)->len = idx + 1; + } + RARRAY(ary)->ptr[idx] = val; +} + +static VALUE +ary_shared_first(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + VALUE nv, result; + long n; + + rb_scan_args(argc, argv, "1", &nv); + n = NUM2LONG(nv); + if (n > RARRAY(ary)->len) { + n = RARRAY(ary)->len; + } + else if (n < 0) { + rb_raise(rb_eArgError, "negative array size"); + } + result = ary_shared_array(rb_cArray, ary); + RARRAY(result)->len = n; + return result; +} + +static VALUE +ary_shared_last(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + VALUE result = ary_shared_first(argc, argv, ary); + + RARRAY(result)->ptr += RARRAY(ary)->len - RARRAY(result)->len; + return result; +} + +/* + * call-seq: + * array << obj -> array + * + * Append---Pushes the given object on to the end of this array. This + * expression returns the array itself, so several appends + * may be chained together. + * + * [ 1, 2 ] << "c" << "d" << [ 3, 4 ] + * #=> [ 1, 2, "c", "d", [ 3, 4 ] ] + * + */ + +VALUE +rb_ary_push(ary, item) + VALUE ary; + VALUE item; +{ + rb_ary_store(ary, RARRAY(ary)->len, item); + return ary; +} + +/* + * call-seq: + * array.push(obj, ... ) -> array + * + * Append---Pushes the given object(s) on to the end of this array. This + * expression returns the array itself, so several appends + * may be chained together. + * + * a = [ "a", "b", "c" ] + * a.push("d", "e", "f") + * #=> ["a", "b", "c", "d", "e", "f"] + */ + +static VALUE +rb_ary_push_m(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + while (argc--) { + rb_ary_push(ary, *argv++); + } + return ary; +} + +VALUE +rb_ary_pop(ary) + VALUE ary; +{ + rb_ary_modify_check(ary); + if (RARRAY(ary)->len == 0) return Qnil; + if (!FL_TEST(ary, ELTS_SHARED) && + RARRAY(ary)->len * 2 < RARRAY(ary)->aux.capa && + RARRAY(ary)->aux.capa > ARY_DEFAULT_SIZE) { + RARRAY(ary)->aux.capa = RARRAY(ary)->len * 2; + REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->aux.capa); + } + return RARRAY(ary)->ptr[--RARRAY(ary)->len]; +} + +/* + * call-seq: + * array.pop -> obj or nil + * + * Removes the last element from self and returns it, or + * nil if the array is empty. + * + * a = [ "a", "b", "c", "d" ] + * a.pop #=> "d" + * a.pop(2) #=> ["b", "c"] + * a #=> ["a"] + */ + +static VALUE +rb_ary_pop_m(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + VALUE result; + + if (argc == 0) { + return rb_ary_pop(ary); + } + + rb_ary_modify_check(ary); + + result = ary_shared_last(argc, argv, ary); + RARRAY(ary)->len -= RARRAY(result)->len; + return result; +} + +VALUE +rb_ary_shift(ary) + VALUE ary; +{ + VALUE top; + + rb_ary_modify_check(ary); + if (RARRAY(ary)->len == 0) return Qnil; + top = RARRAY(ary)->ptr[0]; + ary_make_shared(ary); + RARRAY(ary)->ptr++; /* shift ptr */ + RARRAY(ary)->len--; + + return top; +} + +/* + * call-seq: + * array.shift -> obj or nil + * + * Returns the first element of self and removes it (shifting all + * other elements down by one). Returns nil if the array + * is empty. + * + * args = [ "-m", "-q", "filename" ] + * args.shift #=> "-m" + * args #=> ["-q", "filename"] + * + * args = [ "-m", "-q", "filename" ] + * args.shift(2) #=> ["-m", "-q"] + * args #=> ["filename"] + */ + +static VALUE +rb_ary_shift_m(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + VALUE result; + long n; + + if (argc == 0) { + return rb_ary_shift(ary); + } + + rb_ary_modify_check(ary); + + result = ary_shared_first(argc, argv, ary); + n = RARRAY(result)->len; + RARRAY(ary)->ptr += n; + RARRAY(ary)->len -= n; + + return result; +} + +VALUE +rb_ary_unshift(ary, item) + VALUE ary, item; +{ + rb_ary_modify(ary); + if (RARRAY(ary)->len == RARRAY(ary)->aux.capa) { + long capa_inc = RARRAY(ary)->aux.capa / 2; + if (capa_inc < ARY_DEFAULT_SIZE) { + capa_inc = ARY_DEFAULT_SIZE; + } + RARRAY(ary)->aux.capa += capa_inc; + REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->aux.capa); + } + + /* sliding items */ + MEMMOVE(RARRAY(ary)->ptr + 1, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len); + + RARRAY(ary)->len++; + RARRAY(ary)->ptr[0] = item; + + return ary; +} + +/* + * call-seq: + * array.unshift(obj, ...) -> array + * + * Prepends objects to the front of array. + * other elements up one. + * + * a = [ "b", "c", "d" ] + * a.unshift("a") #=> ["a", "b", "c", "d"] + * a.unshift(1, 2) #=> [ 1, 2, "a", "b", "c", "d"] + */ + +static VALUE +rb_ary_unshift_m(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + long len = RARRAY(ary)->len; + + if (argc == 0) return ary; + + /* make rooms by setting the last item */ + rb_ary_store(ary, len + argc - 1, Qnil); + + /* sliding items */ + MEMMOVE(RARRAY(ary)->ptr + argc, RARRAY(ary)->ptr, VALUE, len); + MEMCPY(RARRAY(ary)->ptr, argv, VALUE, argc); + + return ary; +} + +/* faster version - use this if you don't need to treat negative offset */ +static inline VALUE +rb_ary_elt(ary, offset) + VALUE ary; + long offset; +{ + if (RARRAY(ary)->len == 0) return Qnil; + if (offset < 0 || RARRAY(ary)->len <= offset) { + return Qnil; + } + return RARRAY(ary)->ptr[offset]; +} + +VALUE +rb_ary_entry(ary, offset) + VALUE ary; + long offset; +{ + if (offset < 0) { + offset += RARRAY(ary)->len; + } + return rb_ary_elt(ary, offset); +} + +static VALUE +rb_ary_subseq(ary, beg, len) + VALUE ary; + long beg, len; +{ + VALUE klass, ary2, shared; + VALUE *ptr; + + if (beg > RARRAY(ary)->len) return Qnil; + if (beg < 0 || len < 0) return Qnil; + + if (beg + len > RARRAY(ary)->len) { + len = RARRAY(ary)->len - beg; + if (len < 0) + len = 0; + } + klass = rb_obj_class(ary); + if (len == 0) return ary_new(klass, 0); + + shared = ary_make_shared(ary); + ptr = RARRAY(ary)->ptr; + ary2 = ary_alloc(klass); + RARRAY(ary2)->ptr = ptr + beg; + RARRAY(ary2)->len = len; + RARRAY(ary2)->aux.shared = shared; + FL_SET(ary2, ELTS_SHARED); + + return ary2; +} + +/* + * call-seq: + * array[index] -> obj or nil + * array[start, length] -> an_array or nil + * array[range] -> an_array or nil + * array.slice(index) -> obj or nil + * array.slice(start, length) -> an_array or nil + * array.slice(range) -> an_array or nil + * + * Element Reference---Returns the element at _index_, + * or returns a subarray starting at _start_ and + * continuing for _length_ elements, or returns a subarray + * specified by _range_. + * Negative indices count backward from the end of the + * array (-1 is the last element). Returns nil if the index + * (or starting index) are out of range. + * + * a = [ "a", "b", "c", "d", "e" ] + * a[2] + a[0] + a[1] #=> "cab" + * a[6] #=> nil + * a[1, 2] #=> [ "b", "c" ] + * a[1..3] #=> [ "b", "c", "d" ] + * a[4..7] #=> [ "e" ] + * a[6..10] #=> nil + * a[-3, 3] #=> [ "c", "d", "e" ] + * # special cases + * a[5] #=> nil + * a[5, 1] #=> [] + * a[5..10] #=> [] + * + */ + +VALUE +rb_ary_aref(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + VALUE arg; + long beg, len; + + if (argc == 2) { + beg = NUM2LONG(argv[0]); + len = NUM2LONG(argv[1]); + if (beg < 0) { + beg += RARRAY(ary)->len; + } + return rb_ary_subseq(ary, beg, len); + } + if (argc != 1) { + rb_scan_args(argc, argv, "11", 0, 0); + } + arg = argv[0]; + /* special case - speeding up */ + if (FIXNUM_P(arg)) { + return rb_ary_entry(ary, FIX2LONG(arg)); + } + /* check if idx is Range */ + switch (rb_range_beg_len(arg, &beg, &len, RARRAY(ary)->len, 0)) { + case Qfalse: + break; + case Qnil: + return Qnil; + default: + return rb_ary_subseq(ary, beg, len); + } + return rb_ary_entry(ary, NUM2LONG(arg)); +} + +/* + * call-seq: + * array.at(index) -> obj or nil + * + * Returns the element at _index_. A + * negative index counts from the end of _self_. Returns +nil+ + * if the index is out of range. See also Array#[]. + * (Array#at is slightly faster than Array#[], + * as it does not accept ranges and so on.) + * + * a = [ "a", "b", "c", "d", "e" ] + * a.at(0) #=> "a" + * a.at(-1) #=> "e" + */ + +static VALUE +rb_ary_at(ary, pos) + VALUE ary, pos; +{ + return rb_ary_entry(ary, NUM2LONG(pos)); +} + +/* + * call-seq: + * array.first -> obj or nil + * array.first(n) -> an_array + * + * Returns the first element of the array. If the array is empty, + * returns nil. + * + * a = [ "q", "r", "s", "t" ] + * a.first #=> "q" + * a.first(2) #=> ["q", "r"] + */ + +static VALUE +rb_ary_first(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + if (argc == 0) { + if (RARRAY(ary)->len == 0) return Qnil; + return RARRAY(ary)->ptr[0]; + } + else { + return ary_shared_first(argc, argv, ary); + } +} + +/* + * call-seq: + * array.last -> obj or nil + * array.last(n) -> an_array + * + * Returns the last element(s) of self. If the array is empty, + * the first form returns nil. + * + * a = [ "w", "x", "y", "z" ] + * a.last #=> "z" + * a.last(2) #=> ["y", "z"] + */ + +static VALUE +rb_ary_last(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + if (argc == 0) { + if (RARRAY(ary)->len == 0) return Qnil; + return RARRAY(ary)->ptr[RARRAY(ary)->len-1]; + } + else { + return ary_shared_last(argc, argv, ary); + } +} + +/* + * call-seq: + * array.fetch(index) -> obj + * array.fetch(index, default ) -> obj + * array.fetch(index) {|index| block } -> obj + * + * Tries to return the element at position index. If the index + * lies outside the array, the first form throws an + * IndexError exception, the second form returns + * default, and the third form returns the value of invoking + * the block, passing in the index. Negative values of index + * count from the end of the array. + * + * a = [ 11, 22, 33, 44 ] + * a.fetch(1) #=> 22 + * a.fetch(-1) #=> 44 + * a.fetch(4, 'cat') #=> "cat" + * a.fetch(4) { |i| i*i } #=> 16 + */ + +static VALUE +rb_ary_fetch(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + VALUE pos, ifnone; + long block_given; + long idx; + + rb_scan_args(argc, argv, "11", &pos, &ifnone); + block_given = rb_block_given_p(); + if (block_given && argc == 2) { + rb_warn("block supersedes default value argument"); + } + idx = NUM2LONG(pos); + + if (idx < 0) { + idx += RARRAY(ary)->len; + } + if (idx < 0 || RARRAY(ary)->len <= idx) { + if (block_given) return rb_yield(pos); + if (argc == 1) { + rb_raise(rb_eIndexError, "index %ld out of array", idx); + } + return ifnone; + } + return RARRAY(ary)->ptr[idx]; +} + +/* + * call-seq: + * array.index(obj) -> int or nil + * array.index {|item| block} -> int or nil + * + * Returns the index of the first object in self such that is + * == to obj. If a block is given instead of an + * argument, returns first object for which block is true. + * Returns nil if no match is found. + * + * a = [ "a", "b", "c" ] + * a.index("b") #=> 1 + * a.index("z") #=> nil + * a.index{|x|x=="b"} #=> 1 + */ + +static VALUE +rb_ary_index(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + VALUE val; + long i; + + if (rb_scan_args(argc, argv, "01", &val) == 0) { + for (i=0; ilen; i++) { + if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) { + return LONG2NUM(i); + } + } + } + else { + for (i=0; ilen; i++) { + if (rb_equal(RARRAY(ary)->ptr[i], val)) + return LONG2NUM(i); + } + } + return Qnil; +} + +/* + * call-seq: + * array.rindex(obj) -> int or nil + * + * Returns the index of the last object in array + * == to obj. If a block is given instead of an + * argument, returns first object for which block is + * true. Returns nil if no match is found. + * + * a = [ "a", "b", "b", "b", "c" ] + * a.rindex("b") #=> 3 + * a.rindex("z") #=> nil + * a.rindex{|x|x=="b"} #=> 3 + */ + +static VALUE +rb_ary_rindex(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + VALUE val; + long i = RARRAY(ary)->len; + + if (rb_scan_args(argc, argv, "01", &val) == 0) { + while (i--) { + if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) + return LONG2NUM(i); + if (i > RARRAY(ary)->len) { + i = RARRAY(ary)->len; + } + } + } + else { + while (i--) { + if (rb_equal(RARRAY(ary)->ptr[i], val)) + return LONG2NUM(i); + if (i > RARRAY(ary)->len) { + i = RARRAY(ary)->len; + } + } + } + return Qnil; +} + +VALUE +rb_ary_to_ary(obj) + VALUE obj; +{ + if (TYPE(obj) == T_ARRAY) { + return obj; + } + if (rb_respond_to(obj, rb_intern("to_ary"))) { + return to_ary(obj); + } + return rb_ary_new3(1, obj); +} + +static void +rb_ary_splice(ary, beg, len, rpl) + VALUE ary; + long beg, len; + VALUE rpl; +{ + long rlen; + + if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len); + if (beg < 0) { + beg += RARRAY(ary)->len; + if (beg < 0) { + beg -= RARRAY(ary)->len; + rb_raise(rb_eIndexError, "index %ld out of array", beg); + } + } + if (beg + len > RARRAY(ary)->len) { + len = RARRAY(ary)->len - beg; + } + + if (rpl == Qundef) { + rlen = 0; + } + else { + rpl = rb_ary_to_ary(rpl); + rlen = RARRAY(rpl)->len; + } + rb_ary_modify(ary); + + if (beg >= RARRAY(ary)->len) { + len = beg + rlen; + if (len >= RARRAY(ary)->aux.capa) { + REALLOC_N(RARRAY(ary)->ptr, VALUE, len); + RARRAY(ary)->aux.capa = len; + } + rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len, beg - RARRAY(ary)->len); + if (rlen > 0) { + MEMCPY(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen); + } + RARRAY(ary)->len = len; + } + else { + long alen; + + if (beg + len > RARRAY(ary)->len) { + len = RARRAY(ary)->len - beg; + } + + alen = RARRAY(ary)->len + rlen - len; + if (alen >= RARRAY(ary)->aux.capa) { + REALLOC_N(RARRAY(ary)->ptr, VALUE, alen); + RARRAY(ary)->aux.capa = alen; + } + + if (len != rlen) { + MEMMOVE(RARRAY(ary)->ptr + beg + rlen, RARRAY(ary)->ptr + beg + len, + VALUE, RARRAY(ary)->len - (beg + len)); + RARRAY(ary)->len = alen; + } + if (rlen > 0) { + MEMMOVE(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen); + } + } +} + +/* + * call-seq: + * array[index] = obj -> obj + * array[start, length] = obj or an_array or nil -> obj or an_array or nil + * array[range] = obj or an_array or nil -> obj or an_array or nil + * + * Element Assignment---Sets the element at _index_, + * or replaces a subarray starting at _start_ and + * continuing for _length_ elements, or replaces a subarray + * specified by _range_. If indices are greater than + * the current capacity of the array, the array grows + * automatically. A negative indices will count backward + * from the end of the array. Inserts elements if _length_ is + * zero. An +IndexError+ is raised if a negative index points + * past the beginning of the array. See also + * Array#push, and Array#unshift. + * + * a = Array.new + * a[4] = "4"; #=> [nil, nil, nil, nil, "4"] + * a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"] + * a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"] + * a[0, 2] = "?" #=> ["?", 2, nil, "4"] + * a[0..2] = "A" #=> ["A", "4"] + * a[-1] = "Z" #=> ["A", "Z"] + * a[1..-1] = nil #=> ["A", nil] + * a[1..-1] = [] #=> ["A"] + */ + +static VALUE +rb_ary_aset(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + long offset, beg, len; + + if (argc == 3) { + rb_ary_splice(ary, NUM2LONG(argv[0]), NUM2LONG(argv[1]), argv[2]); + return argv[2]; + } + if (argc != 2) { + rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc); + } + if (FIXNUM_P(argv[0])) { + offset = FIX2LONG(argv[0]); + goto fixnum; + } + if (rb_range_beg_len(argv[0], &beg, &len, RARRAY(ary)->len, 1)) { + /* check if idx is Range */ + rb_ary_splice(ary, beg, len, argv[1]); + return argv[1]; + } + + offset = NUM2LONG(argv[0]); +fixnum: + rb_ary_store(ary, offset, argv[1]); + return argv[1]; +} + +/* + * call-seq: + * array.insert(index, obj...) -> array + * + * Inserts the given values before the element with the given index + * (which may be negative). + * + * a = %w{ a b c d } + * a.insert(2, 99) #=> ["a", "b", 99, "c", "d"] + * a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"] + */ + +static VALUE +rb_ary_insert(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + long pos; + + if (argc < 1) { + rb_raise(rb_eArgError, "wrong number of arguments (at least 1)"); + } + pos = NUM2LONG(argv[0]); + if (pos == -1) { + pos = RARRAY(ary)->len; + } + else if (pos < 0) { + pos++; + } + + if (argc == 1) return ary; + rb_ary_splice(ary, pos, 0, rb_ary_new4(argc - 1, argv + 1)); + return ary; +} + +/* + * call-seq: + * array.each {|item| block } -> array + * + * Calls block once for each element in self, passing that + * element as a parameter. + * + * a = [ "a", "b", "c" ] + * a.each {|x| print x, " -- " } + * + * produces: + * + * a -- b -- c -- + */ + +VALUE +rb_ary_each(ary) + VALUE ary; +{ + long i; + + for (i=0; ilen; i++) { + rb_yield(RARRAY(ary)->ptr[i]); + } + return ary; +} + +/* + * call-seq: + * array.each_index {|index| block } -> array + * + * Same as Array#each, but passes the index of the element + * instead of the element itself. + * + * a = [ "a", "b", "c" ] + * a.each_index {|x| print x, " -- " } + * + * produces: + * + * 0 -- 1 -- 2 -- + */ + +static VALUE +rb_ary_each_index(ary) + VALUE ary; +{ + long i; + + for (i=0; ilen; i++) { + rb_yield(LONG2NUM(i)); + } + return ary; +} + +/* + * call-seq: + * array.reverse_each {|item| block } + * + * Same as Array#each, but traverses self in reverse + * order. + * + * a = [ "a", "b", "c" ] + * a.reverse_each {|x| print x, " " } + * + * produces: + * + * c b a + */ + +static VALUE +rb_ary_reverse_each(ary) + VALUE ary; +{ + long len = RARRAY(ary)->len; + + while (len--) { + rb_yield(RARRAY(ary)->ptr[len]); + if (RARRAY(ary)->len < len) { + len = RARRAY(ary)->len; + } + } + return ary; +} + +/* + * call-seq: + * array.length -> int + * + * Returns the number of elements in self. May be zero. + * + * [ 1, 2, 3, 4, 5 ].length #=> 5 + */ + +static VALUE +rb_ary_length(ary) + VALUE ary; +{ + return LONG2NUM(RARRAY(ary)->len); +} + +/* + * call-seq: + * array.empty? -> true or false + * + * Returns true if self array contains no elements. + * + * [].empty? #=> true + */ + +static VALUE +rb_ary_empty_p(ary) + VALUE ary; +{ + if (RARRAY(ary)->len == 0) + return Qtrue; + return Qfalse; +} + +VALUE +rb_ary_dup(ary) + VALUE ary; +{ + VALUE dup = rb_ary_new2(RARRAY(ary)->len); + + DUPSETUP(dup, ary); + MEMCPY(RARRAY(dup)->ptr, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len); + RARRAY(dup)->len = RARRAY(ary)->len; + return dup; +} + +extern VALUE rb_output_fs; + +static VALUE +recursive_join(ary, arg, recur) + VALUE ary; + VALUE *arg; + int recur; +{ + if (recur) { + return rb_str_new2("[...]"); + } + return rb_ary_join(arg[0], arg[1]); +} + +VALUE +rb_ary_join(ary, sep) + VALUE ary, sep; +{ + long len = 1, i; + int taint = Qfalse; + VALUE result, tmp; + + if (RARRAY(ary)->len == 0) return rb_str_new(0, 0); + if (OBJ_TAINTED(ary) || OBJ_TAINTED(sep)) taint = Qtrue; + + for (i=0; ilen; i++) { + tmp = rb_check_string_type(RARRAY(ary)->ptr[i]); + len += NIL_P(tmp) ? 10 : RSTRING(tmp)->len; + } + if (!NIL_P(sep)) { + StringValue(sep); + len += RSTRING(sep)->len * (RARRAY(ary)->len - 1); + } + result = rb_str_buf_new(len); + for (i=0; ilen; i++) { + tmp = RARRAY(ary)->ptr[i]; + switch (TYPE(tmp)) { + case T_STRING: + break; + case T_ARRAY: + { + VALUE args[2]; + + args[0] = tmp; + args[1] = sep; + tmp = rb_exec_recursive(recursive_join, ary, (VALUE)args); + } + break; + default: + tmp = rb_obj_as_string(tmp); + } + if (i > 0 && !NIL_P(sep)) + rb_str_buf_append(result, sep); + rb_str_buf_append(result, tmp); + if (OBJ_TAINTED(tmp)) taint = Qtrue; + } + + if (taint) OBJ_TAINT(result); + return result; +} + +/* + * call-seq: + * array.join(sep=$,) -> str + * + * Returns a string created by converting each element of the array to + * a string, separated by sep. + * + * [ "a", "b", "c" ].join #=> "abc" + * [ "a", "b", "c" ].join("-") #=> "a-b-c" + */ + +static VALUE +rb_ary_join_m(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + VALUE sep; + + rb_scan_args(argc, argv, "01", &sep); + if (NIL_P(sep)) sep = rb_output_fs; + + return rb_ary_join(ary, sep); +} + +/* + * call-seq: + * array.to_s -> string + * + * Returns _self_.join. + * + * [ "a", "e", "i", "o" ].to_s #=> "aeio" + * + */ + +VALUE +rb_ary_to_s(ary) + VALUE ary; +{ + if (RARRAY(ary)->len == 0) return rb_str_new(0, 0); + + return rb_ary_join(ary, rb_output_fs); +} + +static VALUE +inspect_ary(ary, dummy, recur) + VALUE ary; + VALUE dummy; + int recur; +{ + int tainted = OBJ_TAINTED(ary); + long i; + VALUE s, str; + + if (recur) return rb_tainted_str_new2("[...]"); + str = rb_str_buf_new2("["); + for (i=0; ilen; i++) { + s = rb_inspect(RARRAY(ary)->ptr[i]); + if (OBJ_TAINTED(s)) tainted = Qtrue; + if (i > 0) rb_str_buf_cat2(str, ", "); + rb_str_buf_append(str, s); + } + rb_str_buf_cat2(str, "]"); + if (tainted) OBJ_TAINT(str); + return str; +} + +/* + * call-seq: + * array.inspect -> string + * + * Create a printable version of array. + */ + +static VALUE +rb_ary_inspect(ary) + VALUE ary; +{ + if (RARRAY(ary)->len == 0) return rb_str_new2("[]"); + return rb_exec_recursive(inspect_ary, ary, 0); +} + +/* + * call-seq: + * array.to_a -> array + * + * Returns _self_. If called on a subclass of Array, converts + * the receiver to an Array object. + */ + +static VALUE +rb_ary_to_a(ary) + VALUE ary; +{ + if (rb_obj_class(ary) != rb_cArray) { + VALUE dup = rb_ary_new2(RARRAY(ary)->len); + rb_ary_replace(dup, ary); + return dup; + } + return ary; +} + +/* + * call-seq: + * array.to_ary -> array + * + * Returns _self_. + */ + +static VALUE +rb_ary_to_ary_m(ary) + VALUE ary; +{ + return ary; +} + +VALUE +rb_ary_reverse(ary) + VALUE ary; +{ + VALUE *p1, *p2; + VALUE tmp; + + rb_ary_modify(ary); + if (RARRAY(ary)->len > 1) { + p1 = RARRAY(ary)->ptr; + p2 = p1 + RARRAY(ary)->len - 1; /* points last item */ + + while (p1 < p2) { + tmp = *p1; + *p1++ = *p2; + *p2-- = tmp; + } + } + return ary; +} + +/* + * call-seq: + * array.reverse! -> array + * + * Reverses _self_ in place. + * + * a = [ "a", "b", "c" ] + * a.reverse! #=> ["c", "b", "a"] + * a #=> ["c", "b", "a"] + */ + +static VALUE +rb_ary_reverse_bang(ary) + VALUE ary; +{ + return rb_ary_reverse(ary); +} + +/* + * call-seq: + * array.reverse -> an_array + * + * Returns a new array containing self's elements in reverse order. + * + * [ "a", "b", "c" ].reverse #=> ["c", "b", "a"] + * [ 1 ].reverse #=> [1] + */ + +static VALUE +rb_ary_reverse_m(ary) + VALUE ary; +{ + return rb_ary_reverse(rb_ary_dup(ary)); +} + +struct ary_sort_data { + VALUE ary; + VALUE *ptr; + long len; +}; + +static void +ary_sort_check(data) + struct ary_sort_data *data; +{ + if (RARRAY(data->ary)->ptr != data->ptr || RARRAY(data->ary)->len != data->len) { + rb_raise(rb_eRuntimeError, "array modified during sort"); + } +} + +static int +sort_1(a, b, data) + VALUE *a, *b; + struct ary_sort_data *data; +{ + VALUE retval = rb_yield_values(2, *a, *b); + int n; + + n = rb_cmpint(retval, *a, *b); + ary_sort_check(data); + return n; +} + +static int +sort_2(ap, bp, data) + VALUE *ap, *bp; + struct ary_sort_data *data; +{ + VALUE retval; + VALUE a = *ap, b = *bp; + int n; + + if (FIXNUM_P(a) && FIXNUM_P(b)) { + if ((long)a > (long)b) return 1; + if ((long)a < (long)b) return -1; + return 0; + } + if (TYPE(a) == T_STRING && TYPE(b) == T_STRING) { + return rb_str_cmp(a, b); + } + + retval = rb_funcall(a, id_cmp, 1, b); + n = rb_cmpint(retval, a, b); + ary_sort_check(data); + + return n; +} + +static VALUE +sort_internal(ary) + VALUE ary; +{ + struct ary_sort_data data; + + data.ary = ary; + data.ptr = RARRAY(ary)->ptr; data.len = RARRAY(ary)->len; + qsort(RARRAY(ary)->ptr, RARRAY(ary)->len, sizeof(VALUE), + rb_block_given_p()?sort_1:sort_2, &data); + return ary; +} + +static VALUE +sort_unlock(ary) + VALUE ary; +{ + FL_UNSET(ary, ARY_TMPLOCK); + return ary; +} + +/* + * call-seq: + * array.sort! -> array + * array.sort! {| a,b | block } -> array + * + * Sorts _self_. Comparisons for + * the sort will be done using the <=> operator or using + * an optional code block. The block implements a comparison between + * a and b, returning -1, 0, or +1. See also + * Enumerable#sort_by. + * + * a = [ "d", "a", "e", "c", "b" ] + * a.sort #=> ["a", "b", "c", "d", "e"] + * a.sort {|x,y| y <=> x } #=> ["e", "d", "c", "b", "a"] + */ + +VALUE +rb_ary_sort_bang(ary) + VALUE ary; +{ + rb_ary_modify(ary); + if (RARRAY(ary)->len > 1) { + FL_SET(ary, ARY_TMPLOCK); /* prohibit modification during sort */ + rb_ensure(sort_internal, ary, sort_unlock, ary); + } + return ary; +} + +/* + * call-seq: + * array.sort -> an_array + * array.sort {| a,b | block } -> an_array + * + * Returns a new array created by sorting self. Comparisons for + * the sort will be done using the <=> operator or using + * an optional code block. The block implements a comparison between + * a and b, returning -1, 0, or +1. See also + * Enumerable#sort_by. + * + * a = [ "d", "a", "e", "c", "b" ] + * a.sort #=> ["a", "b", "c", "d", "e"] + * a.sort {|x,y| y <=> x } #=> ["e", "d", "c", "b", "a"] + */ + +VALUE +rb_ary_sort(ary) + VALUE ary; +{ + ary = rb_ary_dup(ary); + rb_ary_sort_bang(ary); + return ary; +} + +/* + * call-seq: + * array.collect {|item| block } -> an_array + * array.map {|item| block } -> an_array + * + * Invokes block once for each element of self. Creates a + * new array containing the values returned by the block. + * See also Enumerable#collect. + * + * a = [ "a", "b", "c", "d" ] + * a.collect {|x| x + "!" } #=> ["a!", "b!", "c!", "d!"] + * a #=> ["a", "b", "c", "d"] + */ + +static VALUE +rb_ary_collect(ary) + VALUE ary; +{ + long i; + VALUE collect; + + if (!rb_block_given_p()) { + return rb_ary_new4(RARRAY(ary)->len, RARRAY(ary)->ptr); + } + + collect = rb_ary_new2(RARRAY(ary)->len); + for (i = 0; i < RARRAY(ary)->len; i++) { + rb_ary_push(collect, rb_yield(RARRAY(ary)->ptr[i])); + } + return collect; +} + +/* + * call-seq: + * array.collect! {|item| block } -> array + * array.map! {|item| block } -> array + * + * Invokes the block once for each element of _self_, replacing the + * element with the value returned by _block_. + * See also Enumerable#collect. + * + * a = [ "a", "b", "c", "d" ] + * a.collect! {|x| x + "!" } + * a #=> [ "a!", "b!", "c!", "d!" ] + */ + +static VALUE +rb_ary_collect_bang(ary) + VALUE ary; +{ + long i; + + rb_ary_modify(ary); + for (i = 0; i < RARRAY(ary)->len; i++) { + rb_ary_store(ary, i, rb_yield(RARRAY(ary)->ptr[i])); + } + return ary; +} + +VALUE +rb_get_values_at(obj, olen, argc, argv, func) + VALUE obj; + long olen; + int argc; + VALUE *argv; + VALUE (*func) _((VALUE,long)); +{ + VALUE result = rb_ary_new2(argc); + long beg, len, i, j; + + for (i=0; i an_array + * + * Returns an array containing the elements in + * _self_ corresponding to the given selector(s). The selectors + * may be either integer indices or ranges. + * See also Array#select. + * + * a = %w{ a b c d e f } + * a.values_at(1, 3, 5) + * a.values_at(1, 3, 5, 7) + * a.values_at(-1, -3, -5, -7) + * a.values_at(1..3, 2...5) + */ + +static VALUE +rb_ary_values_at(argc, argv, ary) + int argc; + VALUE *argv; + VALUE ary; +{ + return rb_get_values_at(ary, RARRAY(ary)->len, argc, argv, rb_ary_entry); +} + +/* + * call-seq: + * array.select {|item| block } -> an_array + * + * Invokes the block passing in successive elements from array, + * returning an array containing those elements for which the block + * returns a true value (equivalent to Enumerable#select). + * + * a = %w{ a b c d e f } + * a.select {|v| v =~ /[aeiou]/} #=> ["a", "e"] + */ + +static VALUE +rb_ary_select(ary) + VALUE ary; +{ + VALUE result; + long i; + + result = rb_ary_new2(RARRAY(ary)->len); + for (i = 0; i < RARRAY(ary)->len; i++) { + if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) { + rb_ary_push(result, rb_ary_elt(ary, i)); + } + } + return result; +} + diff --git a/tests/examplefiles/example.cpp b/tests/examplefiles/example.cpp new file mode 100644 index 0000000..334e7ca --- /dev/null +++ b/tests/examplefiles/example.cpp @@ -0,0 +1,2363 @@ +/*************************************************************************** + ansigenerator.cpp - description + ------------------- + begin : Jul 5 2004 + copyright : (C) 2004 by André Simon + email : andre.simon1@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "ansigenerator.h" + +using namespace std; + +namespace highlight { + + +string AnsiGenerator::getOpenTag(const string&font, + const string&fgCol, const string&bgCol) { + ostringstream s; + s << "\033["< +#include +#include +#include + +#include "codegenerator.h" +#include "charcodes.h" +#include "version.h" + +namespace highlight { + +/** + \brief This class generates ANSI escape sequences. + + It contains information about the resulting document structure (document + header and footer), the colour system, white space handling and text + formatting attributes. + +* @author Andre Simon +*/ + +class AnsiGenerator : public highlight::CodeGenerator + { + public: + + /** Constructor + \param colourTheme Name of Colour theme to use + */ + AnsiGenerator( const string &colourTheme); + AnsiGenerator(); + ~AnsiGenerator(); + + /** prints document header + \param title Title of the document + */ + string getHeader(const string & title); + + /** Prints document footer*/ + string getFooter(); + + /** Prints document body*/ + void printBody(); + + private: + + /** \return escaped character*/ + virtual string maskCharacter(unsigned char ); + + + /** gibt ANSI-"Tags" zurueck (Farbindex+bold+kursiv)*/ + string getOpenTag(const string&font, + const string&fgCol, const string&bgCol=""); + + + + string getMatchingOpenTag(unsigned int styleID); + string getMatchingCloseTag(unsigned int styleID); + }; + +} +#endif +/* + * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved. + * + * ASBeautifier.cpp + * by Tal Davidson (davidsont@bigfoot.com) + * This file is a part of "Artistic Style" - an indentater and reformatter + * of C, C, C# and Java source files. + * + * The "Artistic Style" project, including all files needed to compile it, + * is free software; you can redistribute it and/or use 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. + * + * 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. + * + * You should have received a copy of the GNU General Public + * License along with this program. + * + * Patches: + * 18 March 1999 - Brian Rampel - + * Fixed inverse insertion of spaces vs. tabs when in -t mode. + * 08 may 2004 + * applied ASBeautifier.cpp.BITFIELD.patch.bz2 + */ + +#include "compiler_defines.h" +#include "ASBeautifier.h" + +#include +#include +#include +#include +#include + + +#define INIT_CONTAINER(container, value) {if ( (container) != NULL ) delete (container); (container) = (value); } +#define DELETE_CONTAINER(container) {if ( (container) != NULL ) delete (container) ; } + +#ifdef USES_NAMESPACE +using namespace std; +#endif + + + + +#ifdef USES_NAMESPACE +namespace astyle + { +#endif + + bool ASBeautifier::calledInitStatic = false; + + vector ASBeautifier::headers; + vector ASBeautifier::nonParenHeaders; + vector ASBeautifier::preBlockStatements; + vector ASBeautifier::assignmentOperators; + vector ASBeautifier::nonAssignmentOperators; + + /* + * initialize the static vars + */ + void ASBeautifier::initStatic() + { + if (calledInitStatic) + return; + + calledInitStatic = true; + + headers.push_back(&AS_IF); + headers.push_back(&AS_ELSE); + headers.push_back(&AS_FOR); + headers.push_back(&AS_WHILE); + headers.push_back(&AS_DO); + headers.push_back(&AS_TRY); + headers.push_back(&AS_CATCH); + headers.push_back(&AS_FINALLY); + headers.push_back(&AS_SYNCHRONIZED); + headers.push_back(&AS_SWITCH); + headers.push_back(&AS_CASE); + headers.push_back(&AS_DEFAULT); + headers.push_back(&AS_FOREACH); + headers.push_back(&AS_LOCK); + headers.push_back(&AS_UNSAFE); + headers.push_back(&AS_FIXED); + headers.push_back(&AS_GET); + headers.push_back(&AS_SET); + headers.push_back(&AS_ADD); + headers.push_back(&AS_REMOVE); + //headers.push_back(&AS_PUBLIC); + //headers.push_back(&AS_PRIVATE); + //headers.push_back(&AS_PROTECTED); + + //headers.push_back(&AS_OPERATOR); + headers.push_back(&AS_TEMPLATE); + headers.push_back(&AS_CONST); + /**/ + headers.push_back(&AS_STATIC); + headers.push_back(&AS_EXTERN); + + nonParenHeaders.push_back(&AS_ELSE); + nonParenHeaders.push_back(&AS_DO); + nonParenHeaders.push_back(&AS_TRY); + nonParenHeaders.push_back(&AS_FINALLY); + nonParenHeaders.push_back(&AS_STATIC); + nonParenHeaders.push_back(&AS_CONST); + nonParenHeaders.push_back(&AS_EXTERN); + nonParenHeaders.push_back(&AS_CASE); + nonParenHeaders.push_back(&AS_DEFAULT); + nonParenHeaders.push_back(&AS_UNSAFE); + nonParenHeaders.push_back(&AS_GET); + nonParenHeaders.push_back(&AS_SET); + nonParenHeaders.push_back(&AS_ADD); + nonParenHeaders.push_back(&AS_REMOVE); + + + + nonParenHeaders.push_back(&AS_PUBLIC); + nonParenHeaders.push_back(&AS_PRIVATE); + nonParenHeaders.push_back(&AS_PROTECTED); + nonParenHeaders.push_back(&AS_TEMPLATE); + nonParenHeaders.push_back(&AS_CONST); + /// nonParenHeaders.push_back(&AS_ASM); + + preBlockStatements.push_back(&AS_CLASS); + preBlockStatements.push_back(&AS_STRUCT); + preBlockStatements.push_back(&AS_UNION); + preBlockStatements.push_back(&AS_INTERFACE); + preBlockStatements.push_back(&AS_NAMESPACE); + preBlockStatements.push_back(&AS_THROWS); + preBlockStatements.push_back(&AS_EXTERN); + + assignmentOperators.push_back(&AS_ASSIGN); + assignmentOperators.push_back(&AS_PLUS_ASSIGN); + assignmentOperators.push_back(&AS_MINUS_ASSIGN); + assignmentOperators.push_back(&AS_MULT_ASSIGN); + assignmentOperators.push_back(&AS_DIV_ASSIGN); + assignmentOperators.push_back(&AS_MOD_ASSIGN); + assignmentOperators.push_back(&AS_OR_ASSIGN); + assignmentOperators.push_back(&AS_AND_ASSIGN); + assignmentOperators.push_back(&AS_XOR_ASSIGN); + assignmentOperators.push_back(&AS_GR_GR_GR_ASSIGN); + assignmentOperators.push_back(&AS_GR_GR_ASSIGN); + assignmentOperators.push_back(&AS_LS_LS_LS_ASSIGN); + assignmentOperators.push_back(&AS_LS_LS_ASSIGN); + + assignmentOperators.push_back(&AS_RETURN); + + nonAssignmentOperators.push_back(&AS_EQUAL); + nonAssignmentOperators.push_back(&AS_PLUS_PLUS); + nonAssignmentOperators.push_back(&AS_MINUS_MINUS); + nonAssignmentOperators.push_back(&AS_NOT_EQUAL); + nonAssignmentOperators.push_back(&AS_GR_EQUAL); + nonAssignmentOperators.push_back(&AS_GR_GR_GR); + nonAssignmentOperators.push_back(&AS_GR_GR); + nonAssignmentOperators.push_back(&AS_LS_EQUAL); + nonAssignmentOperators.push_back(&AS_LS_LS_LS); + nonAssignmentOperators.push_back(&AS_LS_LS); + nonAssignmentOperators.push_back(&AS_ARROW); + nonAssignmentOperators.push_back(&AS_AND); + nonAssignmentOperators.push_back(&AS_OR); + } + + /** + * ASBeautifier's constructor + */ + ASBeautifier::ASBeautifier() + { + initStatic(); + + waitingBeautifierStack = NULL; + activeBeautifierStack = NULL; + waitingBeautifierStackLengthStack = NULL; + activeBeautifierStackLengthStack = NULL; + + headerStack = NULL; + tempStacks = NULL; + blockParenDepthStack = NULL; + blockStatementStack = NULL; + parenStatementStack = NULL; + bracketBlockStateStack = NULL; + inStatementIndentStack = NULL; + inStatementIndentStackSizeStack = NULL; + parenIndentStack = NULL; + sourceIterator = NULL; + + isMinimalConditinalIndentSet = false; + shouldForceTabIndentation = false; + + setSpaceIndentation(4); + setMaxInStatementIndentLength(40); + setClassIndent(false); + setSwitchIndent(false); + setCaseIndent(false); + setBlockIndent(false); + setBracketIndent(false); + setNamespaceIndent(false); + setLabelIndent(false); + setEmptyLineFill(false); + setCStyle(); + setPreprocessorIndent(false); + } + + ASBeautifier::ASBeautifier(const ASBeautifier &other) + { + waitingBeautifierStack = NULL; + activeBeautifierStack = NULL; + waitingBeautifierStackLengthStack = NULL; + activeBeautifierStackLengthStack = NULL; + + headerStack = new vector; + *headerStack = *other.headerStack; + + tempStacks = new vector< vector* >; + vector< vector* >::iterator iter; + for (iter = other.tempStacks->begin(); + iter != other.tempStacks->end(); + ++iter) + { + vector *newVec = new vector; + *newVec = **iter; + tempStacks->push_back(newVec); + } + blockParenDepthStack = new vector; + *blockParenDepthStack = *other.blockParenDepthStack; + + blockStatementStack = new vector; + *blockStatementStack = *other.blockStatementStack; + + parenStatementStack = new vector; + *parenStatementStack = *other.parenStatementStack; + + bracketBlockStateStack = new vector; + *bracketBlockStateStack = *other.bracketBlockStateStack; + + inStatementIndentStack = new vector; + *inStatementIndentStack = *other.inStatementIndentStack; + + inStatementIndentStackSizeStack = new vector; + *inStatementIndentStackSizeStack = *other.inStatementIndentStackSizeStack; + + parenIndentStack = new vector; + *parenIndentStack = *other.parenIndentStack; + + sourceIterator = other.sourceIterator; + + indentString = other.indentString; + currentHeader = other.currentHeader; + previousLastLineHeader = other.previousLastLineHeader; + immediatelyPreviousAssignmentOp = other.immediatelyPreviousAssignmentOp; + isInQuote = other.isInQuote; + isInComment = other.isInComment; + isInCase = other.isInCase; + isInQuestion = other.isInQuestion; + isInStatement =other. isInStatement; + isInHeader = other.isInHeader; + isCStyle = other.isCStyle; + isInOperator = other.isInOperator; + isInTemplate = other.isInTemplate; + isInConst = other.isInConst; + classIndent = other.classIndent; + isInClassHeader = other.isInClassHeader; + isInClassHeaderTab = other.isInClassHeaderTab; + switchIndent = other.switchIndent; + caseIndent = other.caseIndent; + namespaceIndent = other.namespaceIndent; + bracketIndent = other.bracketIndent; + blockIndent = other.blockIndent; + labelIndent = other.labelIndent; + preprocessorIndent = other.preprocessorIndent; + parenDepth = other.parenDepth; + indentLength = other.indentLength; + blockTabCount = other.blockTabCount; + leadingWhiteSpaces = other.leadingWhiteSpaces; + maxInStatementIndent = other.maxInStatementIndent; + templateDepth = other.templateDepth; + quoteChar = other.quoteChar; + prevNonSpaceCh = other.prevNonSpaceCh; + currentNonSpaceCh = other.currentNonSpaceCh; + currentNonLegalCh = other.currentNonLegalCh; + prevNonLegalCh = other.prevNonLegalCh; + isInConditional = other.isInConditional; + minConditionalIndent = other.minConditionalIndent; + prevFinalLineSpaceTabCount = other.prevFinalLineSpaceTabCount; + prevFinalLineTabCount = other.prevFinalLineTabCount; + emptyLineFill = other.emptyLineFill; + probationHeader = other.probationHeader; + isInDefine = other.isInDefine; + isInDefineDefinition = other.isInDefineDefinition; + backslashEndsPrevLine = other.backslashEndsPrevLine; + defineTabCount = other.defineTabCount; + } + + /** + * ASBeautifier's destructor + */ + ASBeautifier::~ASBeautifier() + { + DELETE_CONTAINER( headerStack ); + DELETE_CONTAINER( tempStacks ); + DELETE_CONTAINER( blockParenDepthStack ); + DELETE_CONTAINER( blockStatementStack ); + DELETE_CONTAINER( parenStatementStack ); + DELETE_CONTAINER( bracketBlockStateStack ); + DELETE_CONTAINER( inStatementIndentStack ); + DELETE_CONTAINER( inStatementIndentStackSizeStack ); + DELETE_CONTAINER( parenIndentStack ); + + // DELETE_CONTAINER( sourceIterator ); + } + + /** + * initialize the ASBeautifier. + * + * init() should be called every time a ABeautifier object is to start + * beautifying a NEW source file. + * init() recieves a pointer to a DYNAMICALLY CREATED ASSourceIterator object + * that will be used to iterate through the source code. This object will be + * deleted during the ASBeautifier's destruction, and thus should not be + * deleted elsewhere. + * + * @param iter a pointer to the DYNAMICALLY CREATED ASSourceIterator object. + */ + void ASBeautifier::init(ASSourceIterator *iter) + + { + sourceIterator = iter; + init(); + } + + /** + * initialize the ASBeautifier. + */ + void ASBeautifier::init() + { + INIT_CONTAINER( waitingBeautifierStack, new vector ); + INIT_CONTAINER( activeBeautifierStack, new vector ); + + INIT_CONTAINER( waitingBeautifierStackLengthStack, new vector ); + INIT_CONTAINER( activeBeautifierStackLengthStack, new vector ); + + INIT_CONTAINER( headerStack, new vector ); + INIT_CONTAINER( tempStacks, new vector< vector* > ); + tempStacks->push_back(new vector); + + INIT_CONTAINER( blockParenDepthStack, new vector ); + INIT_CONTAINER( blockStatementStack, new vector ); + INIT_CONTAINER( parenStatementStack, new vector ); + + INIT_CONTAINER( bracketBlockStateStack, new vector ); + bracketBlockStateStack->push_back(true); + + INIT_CONTAINER( inStatementIndentStack, new vector ); + INIT_CONTAINER( inStatementIndentStackSizeStack, new vector ); + inStatementIndentStackSizeStack->push_back(0); + INIT_CONTAINER( parenIndentStack, new vector ); + + immediatelyPreviousAssignmentOp = NULL; + previousLastLineHeader = NULL; + + isInQuote = false; + isInComment = false; + isInStatement = false; + isInCase = false; + isInQuestion = false; + isInClassHeader = false; + isInClassHeaderTab = false; + isInHeader = false; + isInOperator = false; + isInTemplate = false; + isInConst = false; + isInConditional = false; + templateDepth = 0; + parenDepth=0; + blockTabCount = 0; + leadingWhiteSpaces = 0; + prevNonSpaceCh = '{'; + currentNonSpaceCh = '{'; + prevNonLegalCh = '{'; + currentNonLegalCh = '{'; + prevFinalLineSpaceTabCount = 0; + prevFinalLineTabCount = 0; + probationHeader = NULL; + backslashEndsPrevLine = false; + isInDefine = false; + isInDefineDefinition = false; + defineTabCount = 0; + } + + /** + * set indentation style to ANSI C/C++. + */ + void ASBeautifier::setCStyle() + { + isCStyle = true; + } + + /** + * set indentation style to Java / K&R. + */ + void ASBeautifier::setJavaStyle() + { + isCStyle = false; + } + + /** + * indent using one tab per indentation + */ + void ASBeautifier::setTabIndentation(int length, bool forceTabs) + { + indentString = "\t"; + indentLength = length; + shouldForceTabIndentation = forceTabs; + + if (!isMinimalConditinalIndentSet) + minConditionalIndent = indentLength * 2; + } + + /** + + * indent using a number of spaces per indentation. + * + * @param length number of spaces per indent. + */ + void ASBeautifier::setSpaceIndentation(int length) + { + indentString=string(length, ' '); + indentLength = length; + + if (!isMinimalConditinalIndentSet) + minConditionalIndent = indentLength * 2; + } + + /** + * set the maximum indentation between two lines in a multi-line statement. + * + * @param max maximum indentation length. + */ + void ASBeautifier::setMaxInStatementIndentLength(int max) + { + maxInStatementIndent = max; + } + + /** + * set the minimum indentation between two lines in a multi-line condition. + * + * @param min minimal indentation length. + */ + void ASBeautifier::setMinConditionalIndentLength(int min) + { + minConditionalIndent = min; + isMinimalConditinalIndentSet = true; + } + + /** + * set the state of the bracket indentation option. If true, brackets will + * be indented one additional indent. + * + * @param state state of option. + */ + void ASBeautifier::setBracketIndent(bool state) + { + bracketIndent = state; + } + + /** + * set the state of the block indentation option. If true, entire blocks + * will be indented one additional indent, similar to the GNU indent style. + * + * @param state state of option. + */ + void ASBeautifier::setBlockIndent(bool state) + { + if (state) + setBracketIndent(false); // so that we don't have both bracket and block indent + blockIndent = state; + } + + /** + * set the state of the class indentation option. If true, C++ class + * definitions will be indented one additional indent. + * + * @param state state of option. + */ + void ASBeautifier::setClassIndent(bool state) + { + classIndent = state; + } + + /** + * set the state of the switch indentation option. If true, blocks of 'switch' + * statements will be indented one additional indent. + * + * @param state state of option. + */ + void ASBeautifier::setSwitchIndent(bool state) + { + switchIndent = state; + } + + /** + * set the state of the case indentation option. If true, lines of 'case' + * statements will be indented one additional indent. + * + * @param state state of option. + */ + void ASBeautifier::setCaseIndent(bool state) + { + caseIndent = state; + } + /** + * set the state of the namespace indentation option. + * If true, blocks of 'namespace' statements will be indented one + * additional indent. Otherwise, NO indentation will be added. + * + * @param state state of option. + */ + void ASBeautifier::setNamespaceIndent(bool state) + { + namespaceIndent = state; + } + + /** + * set the state of the label indentation option. + * If true, labels will be indented one indent LESS than the + * current indentation level. + * If false, labels will be flushed to the left with NO + * indent at all. + * + * @param state state of option. + */ + void ASBeautifier::setLabelIndent(bool state) + { + labelIndent = state; + } + + /** + * set the state of the preprocessor indentation option. + * If true, multiline #define statements will be indented. + * + * @param state state of option. + */ + void ASBeautifier::setPreprocessorIndent(bool state) + { + preprocessorIndent = state; + } + + /** + * set the state of the empty line fill option. + * If true, empty lines will be filled with the whitespace. + * of their previous lines. + * If false, these lines will remain empty. + * + * @param state state of option. + */ + void ASBeautifier::setEmptyLineFill(bool state) + { + emptyLineFill = state; + } + + /** + * check if there are any indented lines ready to be read by nextLine() + * + * @return are there any indented lines ready? + */ + bool ASBeautifier::hasMoreLines() const + { + return sourceIterator->hasMoreLines(); + } + + /** + * get the next indented line. + * + * @return indented line. + */ + string ASBeautifier::nextLine() + { + return beautify(sourceIterator->nextLine()); + } + + /** + * beautify a line of source code. + * every line of source code in a source code file should be sent + * one after the other to the beautify method. + * + * @return the indented line. + * @param originalLine the original unindented line. + */ + string ASBeautifier::beautify(const string &originalLine) + { + string line; + bool isInLineComment = false; + bool lineStartsInComment = false; + bool isInClass = false; + bool isInSwitch = false; + bool isImmediatelyAfterConst = false; + bool isSpecialChar = false; + + char ch = ' '; + char prevCh; + string outBuffer; // the newly idented line is bufferd here + int tabCount = 0; + const string *lastLineHeader = NULL; + bool closingBracketReached = false; + int spaceTabCount = 0; + char tempCh; + unsigned int headerStackSize = headerStack->size(); + //bool isLineInStatement = isInStatement; + bool shouldIndentBrackettedLine = true; + int lineOpeningBlocksNum = 0; + int lineClosingBlocksNum = 0; + bool previousLineProbation = (probationHeader != NULL); + unsigned int i; + + currentHeader = NULL; + + lineStartsInComment = isInComment; + + // handle and remove white spaces around the line: + // If not in comment, first find out size of white space before line, + // so that possible comments starting in the line continue in + // relation to the preliminary white-space. + if (!isInComment) + { + leadingWhiteSpaces = 0; + while (leadingWhiteSpacesinit(); + //defineBeautifier->isInDefineDefinition = true; + //defineBeautifier->beautify(""); + activeBeautifierStack->push_back(defineBeautifier); + } + else + { + // the is the cloned beautifier that is in charge of indenting the #define. + isInDefine = true; + } + } + else if (preproc.COMPARE(0, 2, string("if")) == 0) + { + // push a new beautifier into the stack + waitingBeautifierStackLengthStack->push_back(waitingBeautifierStack->size()); + activeBeautifierStackLengthStack->push_back(activeBeautifierStack->size()); + waitingBeautifierStack->push_back(new ASBeautifier(*this)); + } + else if (preproc.COMPARE(0, 4/*2*/, string("else")) == 0) + { + if (!waitingBeautifierStack->empty()) + { + // MOVE current waiting beautifier to active stack. + activeBeautifierStack->push_back(waitingBeautifierStack->back()); + waitingBeautifierStack->pop_back(); + } + } + else if (preproc.COMPARE(0, 4, string("elif")) == 0) + { + if (!waitingBeautifierStack->empty()) + { + // append a COPY current waiting beautifier to active stack, WITHOUT deleting the original. + activeBeautifierStack->push_back( new ASBeautifier( *(waitingBeautifierStack->back()) ) ); + } + } + else if (preproc.COMPARE(0, 5, string("endif")) == 0) + { + unsigned int stackLength; + ASBeautifier *beautifier; + + if (!waitingBeautifierStackLengthStack->empty()) + { + stackLength = waitingBeautifierStackLengthStack->back(); + waitingBeautifierStackLengthStack->pop_back(); + while (waitingBeautifierStack->size() > stackLength) + { + beautifier = waitingBeautifierStack->back(); + waitingBeautifierStack->pop_back(); + delete beautifier; + } + } + + if (!activeBeautifierStackLengthStack->empty()) + { + stackLength = activeBeautifierStackLengthStack->back(); + activeBeautifierStackLengthStack->pop_back(); + while (activeBeautifierStack->size() > stackLength) + { + beautifier = activeBeautifierStack->back(); + activeBeautifierStack->pop_back(); + delete beautifier; + } + } + + + } + } + + // check if the last char is a backslash + if(line.length() > 0) + backslashEndsPrevLine = (line[line.length() - 1] == '\\'); + else + backslashEndsPrevLine = false; + + // check if this line ends a multi-line #define + // if so, use the #define's cloned beautifier for the line's indentation + // and then remove it from the active beautifier stack and delete it. + if (!backslashEndsPrevLine && isInDefineDefinition && !isInDefine) + { + string beautifiedLine; + ASBeautifier *defineBeautifier; + + isInDefineDefinition = false; + defineBeautifier = activeBeautifierStack->back(); + activeBeautifierStack->pop_back(); + + beautifiedLine = defineBeautifier->beautify(line); + delete defineBeautifier; + return beautifiedLine; + } + + // unless this is a multi-line #define, return this precompiler line as is. + if (!isInDefine && !isInDefineDefinition) + return originalLine; + } + + // if there exists any worker beautifier in the activeBeautifierStack, + // then use it instead of me to indent the current line. + if (!isInDefine && activeBeautifierStack != NULL && !activeBeautifierStack->empty()) + { + return activeBeautifierStack->back()->beautify(line); + } + + // calculate preliminary indentation based on data from past lines + if (!inStatementIndentStack->empty()) + spaceTabCount = inStatementIndentStack->back(); + + + for (i=0; i0 && (*headerStack)[i-1] != &AS_OPEN_BRACKET + && (*headerStack)[i] == &AS_OPEN_BRACKET))) + ++tabCount; + + if (isCStyle && !namespaceIndent && i >= 1 + && (*headerStack)[i-1] == &AS_NAMESPACE + && (*headerStack)[i] == &AS_OPEN_BRACKET) + --tabCount; + + if (isCStyle && i >= 1 + && (*headerStack)[i-1] == &AS_CLASS + && (*headerStack)[i] == &AS_OPEN_BRACKET ) + { + if (classIndent) + ++tabCount; + isInClass = true; + } + + // is the switchIndent option is on, indent switch statements an additional indent. + else if (switchIndent && i > 1 && + (*headerStack)[i-1] == &AS_SWITCH && + (*headerStack)[i] == &AS_OPEN_BRACKET + ) + { + ++tabCount; + isInSwitch = true; + } + + } + + if (!lineStartsInComment + && isCStyle + && isInClass + && classIndent + && headerStackSize >= 2 + &&(*headerStack)[headerStackSize-2] == &AS_CLASS + && (*headerStack)[headerStackSize-1] == &AS_OPEN_BRACKET + && line[0] == '}') + --tabCount; + + else if (!lineStartsInComment + && isInSwitch + && switchIndent + && headerStackSize >= 2 + && (*headerStack)[headerStackSize-2] == &AS_SWITCH + && (*headerStack)[headerStackSize-1] == &AS_OPEN_BRACKET + && line[0] == '}') + --tabCount; + + if (isInClassHeader) + { + isInClassHeaderTab = true; + tabCount += 2; + } + + if (isInConditional) + { + --tabCount; + } + + + // parse characters in the current line. + + for (i=0; ipush_back(probationHeader); + + // handle the specific probation header + isInConditional = (probationHeader == &AS_SYNCHRONIZED); + if (probationHeader == &AS_CONST) + isImmediatelyAfterConst = true; + // isInConst = true; + /* TODO: + * There is actually no more need for the global isInConst variable. + * The only reason for checking const is to see if there is a const + * immediately before an open-bracket. + * Since CONST is now put into probation and is checked during itspost-char, + * isImmediatelyAfterConst can be set by its own... + */ + + isInStatement = false; + // if the probation comes from the previous line, then indent by 1 tab count. + if (previousLineProbation && ch == '{') + tabCount++; + previousLineProbation = false; + } + + // dismiss the probation header + probationHeader = NULL; + } + + prevNonSpaceCh = currentNonSpaceCh; + currentNonSpaceCh = ch; + if (!isLegalNameChar(ch) && ch != ',' && ch != ';' ) + { + prevNonLegalCh = currentNonLegalCh; + currentNonLegalCh = ch; + } + + //if (isInConst) + //{ + // isInConst = false; + // isImmediatelyAfterConst = true; + //} + + if (isInHeader) + { + isInHeader = false; + currentHeader = headerStack->back(); + } + else + currentHeader = NULL; + + if (isCStyle && isInTemplate + && (ch == '<' || ch == '>') + && findHeader(line, i, nonAssignmentOperators) == NULL) //; + { + if (ch == '<') + { + ++templateDepth; + } + else if (ch == '>') + { + if (--templateDepth <= 0) + { + if (isInTemplate) + ch = ';'; + else + ch = 't'; + isInTemplate = false; + templateDepth = 0; + } + + } + } + + // handle parenthesies + if (ch == '(' || ch == '[' || ch == ')' || ch == ']') + { + if (ch == '(' || ch == '[') + { + if (parenDepth == 0) + { + parenStatementStack->push_back(isInStatement); + isInStatement = true; + } + parenDepth++; + + inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size()); + + if (currentHeader != NULL) + registerInStatementIndent(line, i, spaceTabCount, minConditionalIndent/*indentLength*2*/, true); + else + registerInStatementIndent(line, i, spaceTabCount, 0, true); + } + else if (ch == ')' || ch == ']') + { + parenDepth--; + if (parenDepth == 0) + { + isInStatement = parenStatementStack->back(); + parenStatementStack->pop_back(); + ch = ' '; + + isInConditional = false; + } + + if (!inStatementIndentStackSizeStack->empty()) + { + unsigned int previousIndentStackSize = inStatementIndentStackSizeStack->back(); + inStatementIndentStackSizeStack->pop_back(); + while (previousIndentStackSize < inStatementIndentStack->size()) + inStatementIndentStack->pop_back(); + + if (!parenIndentStack->empty()) + { + int poppedIndent = parenIndentStack->back(); + parenIndentStack->pop_back(); + + if (i == 0) + spaceTabCount = poppedIndent; + } + } + } + + continue; + } + + + if (ch == '{') + { + bool isBlockOpener = false; + + // first, check if '{' is a block-opener or an static-array opener + isBlockOpener = ( (prevNonSpaceCh == '{' && bracketBlockStateStack->back()) + || prevNonSpaceCh == '}' + || prevNonSpaceCh == ')' + || prevNonSpaceCh == ';' + || isInClassHeader + || isBlockOpener + || isImmediatelyAfterConst + || (isInDefine && + (prevNonSpaceCh == '(' + || prevNonSpaceCh == '_' + || isalnum(prevNonSpaceCh))) ); + + isInClassHeader = false; + if (!isBlockOpener && currentHeader != NULL) + { + for (unsigned int n=0; n < nonParenHeaders.size(); n++) + if (currentHeader == nonParenHeaders[n]) + { + isBlockOpener = true; + break; + } + } + bracketBlockStateStack->push_back(isBlockOpener); + if (!isBlockOpener) + { + inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size()); + registerInStatementIndent(line, i, spaceTabCount, 0, true); + parenDepth++; + if (i == 0) + shouldIndentBrackettedLine = false; + + continue; + } + + // this bracket is a block opener... + + ++lineOpeningBlocksNum; + + if (isInClassHeader) + isInClassHeader = false; + if (isInClassHeaderTab) + { + isInClassHeaderTab = false; + tabCount -= 2; + } + + blockParenDepthStack->push_back(parenDepth); + blockStatementStack->push_back(isInStatement); + + inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size()); + + blockTabCount += isInStatement? 1 : 0; + parenDepth = 0; + isInStatement = false; + + tempStacks->push_back(new vector); + headerStack->push_back(&AS_OPEN_BRACKET); + lastLineHeader = &AS_OPEN_BRACKET; // <------ + + continue; + } + + //check if a header has been reached + if (prevCh == ' ') + { + bool isIndentableHeader = true; + const string *newHeader = findHeader(line, i, headers); + if (newHeader != NULL) + { + // if we reached here, then this is a header... + isInHeader = true; + + vector *lastTempStack; + if (tempStacks->empty()) + lastTempStack = NULL; + else + lastTempStack = tempStacks->back(); + + // if a new block is opened, push a new stack into tempStacks to hold the + // future list of headers in the new block. + + // take care of the special case: 'else if (...)' + if (newHeader == &AS_IF && lastLineHeader == &AS_ELSE) + { + //spaceTabCount += indentLength; // to counter the opposite addition that occurs when the 'if' is registered below... + headerStack->pop_back(); + } + + // take care of 'else' + else if (newHeader == &AS_ELSE) + { + if (lastTempStack != NULL) + { + int indexOfIf = indexOf(*lastTempStack, &AS_IF); // <--- + if (indexOfIf != -1) + { + // recreate the header list in headerStack up to the previous 'if' + // from the temporary snapshot stored in lastTempStack. + int restackSize = lastTempStack->size() - indexOfIf - 1; + for (int r=0; rpush_back(lastTempStack->back()); + lastTempStack->pop_back(); + } + if (!closingBracketReached) + tabCount += restackSize; + } + /* + * If the above if is not true, i.e. no 'if' before the 'else', + * then nothing beautiful will come out of this... + * I should think about inserting an Exception here to notify the caller of this... + */ + } + } + + // check if 'while' closes a previous 'do' + else if (newHeader == &AS_WHILE) + { + if (lastTempStack != NULL) + { + int indexOfDo = indexOf(*lastTempStack, &AS_DO); // <--- + if (indexOfDo != -1) + { + // recreate the header list in headerStack up to the previous 'do' + // from the temporary snapshot stored in lastTempStack. + int restackSize = lastTempStack->size() - indexOfDo - 1; + for (int r=0; rpush_back(lastTempStack->back()); + lastTempStack->pop_back(); + } + if (!closingBracketReached) + tabCount += restackSize; + } + } + } + // check if 'catch' closes a previous 'try' or 'catch' + else if (newHeader == &AS_CATCH || newHeader == &AS_FINALLY) + { + if (lastTempStack != NULL) + { + int indexOfTry = indexOf(*lastTempStack, &AS_TRY); + if (indexOfTry == -1) + indexOfTry = indexOf(*lastTempStack, &AS_CATCH); + if (indexOfTry != -1) + { + // recreate the header list in headerStack up to the previous 'try' + // from the temporary snapshot stored in lastTempStack. + int restackSize = lastTempStack->size() - indexOfTry - 1; + for (int r=0; rpush_back(lastTempStack->back()); + lastTempStack->pop_back(); + } + + if (!closingBracketReached) + tabCount += restackSize; + } + } + } + else if (newHeader == &AS_CASE) + { + isInCase = true; + if (!caseIndent) + --tabCount; + } + else if(newHeader == &AS_DEFAULT) + { + isInCase = true; + if (!caseIndent) + --tabCount; + } + else if (newHeader == &AS_PUBLIC || newHeader == &AS_PROTECTED || newHeader == &AS_PRIVATE) + { + if (isCStyle && !isInClassHeader) + --tabCount; + isIndentableHeader = false; + } + //else if ((newHeader == &STATIC || newHeader == &SYNCHRONIZED) && + // !headerStack->empty() && + // (headerStack->back() == &STATIC || headerStack->back() == &SYNCHRONIZED)) + //{ + // isIndentableHeader = false; + //} + else if (newHeader == &AS_STATIC + || newHeader == &AS_SYNCHRONIZED + || (newHeader == &AS_CONST && isCStyle)) + { + if (!headerStack->empty() && + (headerStack->back() == &AS_STATIC + || headerStack->back() == &AS_SYNCHRONIZED + || headerStack->back() == &AS_CONST)) + { + isIndentableHeader = false; + } + else + { + isIndentableHeader = false; + probationHeader = newHeader; + } + } + else if (newHeader == &AS_CONST) + { + // this will be entered only if NOT in C style + // since otherwise the CONST would be found to be a probstion header... + + //if (isCStyle) + // isInConst = true; + isIndentableHeader = false; + } + /* + else if (newHeader == &OPERATOR) + { + if (isCStyle) + isInOperator = true; + isIndentableHeader = false; + } + */ + else if (newHeader == &AS_TEMPLATE) + { + if (isCStyle) + isInTemplate = true; + isIndentableHeader = false; + } + + + if (isIndentableHeader) + { + // 3.2.99 + //spaceTabCount-=indentLength; + headerStack->push_back(newHeader); + isInStatement = false; + if (indexOf(nonParenHeaders, newHeader) == -1) + { + isInConditional = true; + } + lastLineHeader = newHeader; + } + else + isInHeader = false; + + //lastLineHeader = newHeader; + + outBuffer.append(newHeader->substr(1)); + i += newHeader->length() - 1; + + continue; + } + } + + if (isCStyle && !isalpha(prevCh) + && line.COMPARE(i, 8, AS_OPERATOR) == 0 && !isalnum(line[i+8])) + { + isInOperator = true; + outBuffer.append(AS_OPERATOR.substr(1)); + i += 7; + continue; + } + + if (ch == '?') + isInQuestion = true; + + + // special handling of 'case' statements + if (ch == ':') + { + if (line.length() > i+1 && line[i+1] == ':') // look for :: + { + ++i; + outBuffer.append(1, ':'); + ch = ' '; + continue; + } + + else if (isCStyle && isInClass && prevNonSpaceCh != ')') + { + // BEGIN Content of ASBeautifier.cpp.BITFIELD.patch: + + unsigned int chIndex; + char nextCh = 0; + for (chIndex = i+1; chIndex < line.length(); chIndex++) + if (!isWhiteSpace(line[chIndex])) + break; + if (chIndex< line.length()) + nextCh = line[chIndex]; + int nWord =0; + for (chIndex = 0; chIndex < i; chIndex++) + { + if (!isWhiteSpace(line[chIndex])) + { + nWord ++; + while (!isWhiteSpace(line[++chIndex])); + } + } + if ((nextCh >= '0' && nextCh <= '9') || (nWord >1)) + continue; + // END Content of ASBeautifier.cpp.BITFIELD.patch: + + --tabCount; + // found a 'private:' or 'public:' inside a class definition + // so do nothing special + } + + else if (isCStyle && isInClassHeader) + { + + // found a 'class A : public B' definition + // so do nothing special + } + + else if (isInQuestion) + { + isInQuestion = false; + } + else if (isCStyle && prevNonSpaceCh == ')') + { + isInClassHeader = true; + if (i==0) + tabCount += 2; + } + else + { + currentNonSpaceCh = ';'; // so that brackets after the ':' will appear as block-openers + if (isInCase) + { + isInCase = false; + ch = ';'; // from here on, treat char as ';' + } + // BEGIN content of ASBeautifier.cpp.BITFIELD.patch.bz2 + else // bitfield or labels + { + unsigned int chIndex; + char nextCh = 0; + for (chIndex = i+1; (isCStyle && chIndex < line.length()); chIndex++) + if (!isWhiteSpace(line[chIndex])) + break; + if (chIndex< line.length()) + nextCh = line[chIndex]; + + int nWord =0; + for (chIndex = 0; chIndex < i; chIndex++) + { + if (!isWhiteSpace(line[chIndex])) + { + nWord ++; + while (!isWhiteSpace(line[++chIndex])); + } + } + if (isCStyle && (nextCh >= '0' && nextCh <= '9') || (nWord >1)) + { + continue; + } + // END content of ASASBeautifier.cpp.BITFIELD.patch.bz2 + + else // is in a label (e.g. 'label1:') + { + if (labelIndent) + --tabCount; // unindent label by one indent + else + tabCount = 0; // completely flush indent to left + } + + // BEGIN content of ASASBeautifier.cpp.BITFIELD.patch.bz2 + } + // END content of ASASBeautifier.cpp.BITFIELD.patch.bz2 + + } + } + + if ((ch == ';' || (parenDepth>0 && ch == ',')) && !inStatementIndentStackSizeStack->empty()) + while ((unsigned int)inStatementIndentStackSizeStack->back() + (parenDepth>0 ? 1 : 0) < inStatementIndentStack->size()) + inStatementIndentStack->pop_back(); + + + // handle ends of statements + if ( (ch == ';' && parenDepth == 0) || ch == '}'/* || (ch == ',' && parenDepth == 0)*/) + { + if (ch == '}') + { + // first check if this '}' closes a previous block, or a static array... + if (!bracketBlockStateStack->empty()) + { + bool bracketBlockState = bracketBlockStateStack->back(); + bracketBlockStateStack->pop_back(); + if (!bracketBlockState) + { + if (!inStatementIndentStackSizeStack->empty()) + { + // this bracket is a static array + + unsigned int previousIndentStackSize = inStatementIndentStackSizeStack->back(); + inStatementIndentStackSizeStack->pop_back(); + while (previousIndentStackSize < inStatementIndentStack->size()) + inStatementIndentStack->pop_back(); + parenDepth--; + if (i == 0) + shouldIndentBrackettedLine = false; + + if (!parenIndentStack->empty()) + { + int poppedIndent = parenIndentStack->back(); + parenIndentStack->pop_back(); + if (i == 0) + spaceTabCount = poppedIndent; + } + } + continue; + } + } + + // this bracket is block closer... + + ++lineClosingBlocksNum; + + if(!inStatementIndentStackSizeStack->empty()) + inStatementIndentStackSizeStack->pop_back(); + + if (!blockParenDepthStack->empty()) + { + parenDepth = blockParenDepthStack->back(); + blockParenDepthStack->pop_back(); + isInStatement = blockStatementStack->back(); + blockStatementStack->pop_back(); + + if (isInStatement) + blockTabCount--; + } + + closingBracketReached = true; + int headerPlace = indexOf(*headerStack, &AS_OPEN_BRACKET); // <--- + if (headerPlace != -1) + { + const string *popped = headerStack->back(); + while (popped != &AS_OPEN_BRACKET) + { + headerStack->pop_back(); + popped = headerStack->back(); + } + headerStack->pop_back(); + + if (!tempStacks->empty()) + { + vector *temp = tempStacks->back(); + tempStacks->pop_back(); + delete temp; + } + } + + + ch = ' '; // needed due to cases such as '}else{', so that headers ('else' tn tih case) will be identified... + } + + /* + * Create a temporary snapshot of the current block's header-list in the + * uppermost inner stack in tempStacks, and clear the headerStack up to + * the begining of the block. + * Thus, the next future statement will think it comes one indent past + * the block's '{' unless it specifically checks for a companion-header + * (such as a previous 'if' for an 'else' header) within the tempStacks, + * and recreates the temporary snapshot by manipulating the tempStacks. + */ + if (!tempStacks->back()->empty()) + while (!tempStacks->back()->empty()) + tempStacks->back()->pop_back(); + while (!headerStack->empty() && headerStack->back() != &AS_OPEN_BRACKET) + { + tempStacks->back()->push_back(headerStack->back()); + headerStack->pop_back(); + } + + if (parenDepth == 0 && ch == ';') + isInStatement=false; + + isInClassHeader = false; + + continue; + } + + + // check for preBlockStatements ONLY if not within parenthesies + // (otherwise 'struct XXX' statements would be wrongly interpreted...) + if (prevCh == ' ' && !isInTemplate && parenDepth == 0) + { + const string *newHeader = findHeader(line, i, preBlockStatements); + if (newHeader != NULL) + { + isInClassHeader = true; + outBuffer.append(newHeader->substr(1)); + i += newHeader->length() - 1; + //if (isCStyle) + headerStack->push_back(newHeader); + } + } + + // Handle operators + // + + //// // PRECHECK if a '==' or '--' or '++' operator was reached. + //// // If not, then register an indent IF an assignment operator was reached. + //// // The precheck is important, so that statements such as 'i--==2' are not recognized + //// // to have assignment operators (here, '-=') in them . . . + + const string *foundAssignmentOp = NULL; + const string *foundNonAssignmentOp = NULL; + + immediatelyPreviousAssignmentOp = NULL; + + // Check if an operator has been reached. + foundAssignmentOp = findHeader(line, i, assignmentOperators, false); + foundNonAssignmentOp = findHeader(line, i, nonAssignmentOperators, false); + + // Since findHeader's boundry checking was not used above, it is possible + // that both an assignment op and a non-assignment op where found, + // e.g. '>>' and '>>='. If this is the case, treat the LONGER one as the + // found operator. + if (foundAssignmentOp != NULL && foundNonAssignmentOp != NULL) + if (foundAssignmentOp->length() < foundNonAssignmentOp->length()) + foundAssignmentOp = NULL; + else + foundNonAssignmentOp = NULL; + + if (foundNonAssignmentOp != NULL) + { + if (foundNonAssignmentOp->length() > 1) + { + outBuffer.append(foundNonAssignmentOp->substr(1)); + i += foundNonAssignmentOp->length() - 1; + } + } + + else if (foundAssignmentOp != NULL) + + { + if (foundAssignmentOp->length() > 1) + { + outBuffer.append(foundAssignmentOp->substr(1)); + i += foundAssignmentOp->length() - 1; + } + + if (!isInOperator && !isInTemplate) + { + registerInStatementIndent(line, i, spaceTabCount, 0, false); + immediatelyPreviousAssignmentOp = foundAssignmentOp; + isInStatement = true; + } + } + + /* + immediatelyPreviousAssignmentOp = NULL; + bool isNonAssingmentOperator = false; + for (int n = 0; n < nonAssignmentOperators.size(); n++) + if (line.COMPARE(i, nonAssignmentOperators[n]->length(), *(nonAssignmentOperators[n])) == 0) + { + if (nonAssignmentOperators[n]->length() > 1) + { + outBuffer.append(nonAssignmentOperators[n]->substr(1)); + i += nonAssignmentOperators[n]->length() - 1; + } + isNonAssingmentOperator = true; + break; + } + if (!isNonAssingmentOperator) + { + for (int a = 0; a < assignmentOperators.size(); a++) + if (line.COMPARE(i, assignmentOperators[a]->length(), *(assignmentOperators[a])) == 0) + { + if (assignmentOperators[a]->length() > 1) + { + outBuffer.append(assignmentOperators[a]->substr(1)); + i += assignmentOperators[a]->length() - 1; + } + + if (!isInOperator && !isInTemplate) + { + registerInStatementIndent(line, i, spaceTabCount, 0, false); + immediatelyPreviousAssignmentOp = assignmentOperators[a]; + isInStatement = true; + } + break; + } + } + */ + + if (isInOperator) + isInOperator = false; + } + + // handle special cases of unindentation: + + /* + * if '{' doesn't follow an immediately previous '{' in the headerStack + * (but rather another header such as "for" or "if", then unindent it + * by one indentation relative to its block. + */ + // cerr << endl << lineOpeningBlocksNum << " " << lineClosingBlocksNum << " " << previousLastLineHeader << endl; + + // indent #define lines with one less tab + //if (isInDefine) + // tabCount -= defineTabCount-1; + + + if (!lineStartsInComment + && !blockIndent + && outBuffer.length()>0 + && outBuffer[0]=='{' + && !(lineOpeningBlocksNum > 0 && lineOpeningBlocksNum == lineClosingBlocksNum) + && !(headerStack->size() > 1 && (*headerStack)[headerStack->size()-2] == &AS_OPEN_BRACKET) + && shouldIndentBrackettedLine) + --tabCount; + + else if (!lineStartsInComment + && outBuffer.length()>0 + && outBuffer[0]=='}' + && shouldIndentBrackettedLine ) + --tabCount; + + // correctly indent one-line-blocks... + else if (!lineStartsInComment + && outBuffer.length()>0 + && lineOpeningBlocksNum > 0 + && lineOpeningBlocksNum == lineClosingBlocksNum + && previousLastLineHeader != NULL + && previousLastLineHeader != &AS_OPEN_BRACKET) + tabCount -= 1; //lineOpeningBlocksNum - (blockIndent ? 1 : 0); + + if (tabCount < 0) + tabCount = 0; + + // take care of extra bracket indentatation option... + if (bracketIndent && outBuffer.length()>0 && shouldIndentBrackettedLine) + if (outBuffer[0]=='{' || outBuffer[0]=='}') + tabCount++; + + + if (isInDefine) + { + if (outBuffer[0] == '#') + { + string preproc = trim(string(outBuffer.c_str() + 1)); + if (preproc.COMPARE(0, 6, string("define")) == 0) + { + if (!inStatementIndentStack->empty() + && inStatementIndentStack->back() > 0) + { + defineTabCount = tabCount; + } + else + { + defineTabCount = tabCount - 1; + tabCount--; + } + } + } + + tabCount -= defineTabCount; + } + + if (tabCount < 0) + tabCount = 0; + + + // finally, insert indentations into begining of line + + prevFinalLineSpaceTabCount = spaceTabCount; + prevFinalLineTabCount = tabCount; + + if (shouldForceTabIndentation) + { + tabCount += spaceTabCount / indentLength; + spaceTabCount = spaceTabCount % indentLength; + } + + outBuffer = preLineWS(spaceTabCount,tabCount) + outBuffer; + + if (lastLineHeader != NULL) + previousLastLineHeader = lastLineHeader; + + return outBuffer; + } + + + string ASBeautifier::preLineWS(int spaceTabCount, int tabCount) + { + string ws; + + for (int i=0; i 0) + ws += string(" "); + + return ws; + + } + + /** + * register an in-statement indent. + */ + void ASBeautifier::registerInStatementIndent(const string &line, int i, int spaceTabCount, + int minIndent, bool updateParenStack) + { + int inStatementIndent; + int remainingCharNum = line.length() - i; + int nextNonWSChar = 1; + + nextNonWSChar = getNextProgramCharDistance(line, i); + + // if indent is around the last char in the line, indent instead 2 spaces from the previous indent + if (nextNonWSChar == remainingCharNum) + { + int previousIndent = spaceTabCount; + if (!inStatementIndentStack->empty()) + previousIndent = inStatementIndentStack->back(); + + inStatementIndentStack->push_back(/*2*/ indentLength + previousIndent ); + if (updateParenStack) + parenIndentStack->push_back( previousIndent ); + return; + } + + if (updateParenStack) + parenIndentStack->push_back(i+spaceTabCount); + + inStatementIndent = i + nextNonWSChar + spaceTabCount; + + if (i + nextNonWSChar < minIndent) + inStatementIndent = minIndent + spaceTabCount; + + if (i + nextNonWSChar > maxInStatementIndent) + inStatementIndent = indentLength*2 + spaceTabCount; + + + + if (!inStatementIndentStack->empty() && + inStatementIndent < inStatementIndentStack->back()) + inStatementIndent = inStatementIndentStack->back(); + + inStatementIndentStack->push_back(inStatementIndent); + } + + /** + * get distance to the next non-white sspace, non-comment character in the line. + * if no such character exists, return the length remaining to the end of the line. + */ + int ASBeautifier::getNextProgramCharDistance(const string &line, int i) + { + bool inComment = false; + int remainingCharNum = line.length() - i; + int charDistance = 1; + int ch; + + for (charDistance = 1; charDistance < remainingCharNum; charDistance++) + { + ch = line[i + charDistance]; + if (inComment) + { + if (line.COMPARE(i + charDistance, 2, AS_CLOSE_COMMENT) == 0) + { + charDistance++; + inComment = false; + } + continue; + } + else if (isWhiteSpace(ch)) + continue; + else if (ch == '/') + { + if (line.COMPARE(i + charDistance, 2, AS_OPEN_LINE_COMMENT) == 0) + return remainingCharNum; + else if (line.COMPARE(i + charDistance, 2, AS_OPEN_COMMENT) == 0) + { + charDistance++; + inComment = true; + } + } + else + return charDistance; + } + + return charDistance; + } + + + /** + * check if a specific character can be used in a legal variable/method/class name + * + * @return legality of the char. + * @param ch the character to be checked. + */ + bool ASBeautifier::isLegalNameChar(char ch) const + { + return (isalnum(ch) //(ch>='a' && ch<='z') || (ch>='A' && ch<='Z') || (ch>='0' && ch<='9') || + || ch=='.' || ch=='_' || (!isCStyle && ch=='$') || (isCStyle && ch=='~')); + } + + + /** + * check if a specific line position contains a header, out of several possible headers. + * + * @return a pointer to the found header. if no header was found then return NULL. + */ + const string *ASBeautifier::findHeader(const string &line, int i, const vector &possibleHeaders, bool checkBoundry) + { + int maxHeaders = possibleHeaders.size(); + const string *header = NULL; + int p; + + for (p=0; p < maxHeaders; p++) + { + header = possibleHeaders[p]; + + if (line.COMPARE(i, header->length(), *header) == 0) + { + // check that this is a header and not a part of a longer word + // (e.g. not at its begining, not at its middle...) + + int lineLength = line.length(); + int headerEnd = i + header->length(); + char startCh = (*header)[0]; // first char of header + char endCh = 0; // char just after header + char prevCh = 0; // char just before header + + if (headerEnd < lineLength) + { + endCh = line[headerEnd]; + } + if (i > 0) + { + prevCh = line[i-1]; + } + + if (!checkBoundry) + { + return header; + } + else if (prevCh != 0 + && isLegalNameChar(startCh) + && isLegalNameChar(prevCh)) + { + return NULL; + } + else if (headerEnd >= lineLength + || !isLegalNameChar(startCh) + || !isLegalNameChar(endCh)) + { + return header; + } + else + { + return NULL; + } + } + } + + return NULL; + } + + + /** + * check if a specific character can be used in a legal variable/method/class name + * + * @return legality of the char. + * @param ch the character to be checked. + */ + bool ASBeautifier::isWhiteSpace(char ch) const + { + return (ch == ' ' || ch == '\t'); + } + + /** + * find the index number of a string element in a container of strings + * + * @return the index number of element in the ocntainer. -1 if element not found. + * @param container a vector of strings. + * @param element the element to find . + */ + int ASBeautifier::indexOf(vector &container, const string *element) + { + vector::const_iterator where; + + where= find(container.begin(), container.end(), element); + if (where == container.end()) + return -1; + else + return where - container.begin(); + } + + /** + * trim removes the white space surrounding a line. + * + * @return the trimmed line. + * @param str the line to trim. + */ + string ASBeautifier::trim(const string &str) + { + + int start = 0; + int end = str.length() - 1; + + while (start < end && isWhiteSpace(str[start])) + start++; + + while (start <= end && isWhiteSpace(str[end])) + end--; + + string returnStr(str, start, end+1-start); + return returnStr; + } + +#ifdef USES_NAMESPACE +} +#endif +/* + * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved. + * + * compiler_defines.h (1 January 1999) + * by Tal Davidson (davidsont@bigfoot.com) + * This file is a part of "Artistic Style" - an indentater and reformatter + * of C, C++, C# and Java source files. + * + * The "Artistic Style" project, including all files needed to compile it, + * is free software; you can redistribute it and/or use 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. + * + * 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. + * + * You should have received a copy of the GNU General Public + * License along with this program. + */ + + +#ifndef ASBEAUTIFIER_H +#define ASBEAUTIFIER_H + +#include "ASResource.h" +#include "compiler_defines.h" +#include "ASSourceIterator.h" + +#include +#include + + +using namespace std; + +namespace astyle + { + + enum BracketMode { NONE_MODE, ATTACH_MODE, BREAK_MODE, BDAC_MODE }; + enum BracketType { NULL_TYPE = 0, + DEFINITION_TYPE = 1, + COMMAND_TYPE = 2, + ARRAY_TYPE = 4, + SINGLE_LINE_TYPE = 8}; + + + class ASBeautifier : protected ASResource + { + public: + ASBeautifier(); + virtual ~ASBeautifier(); + virtual void init(ASSourceIterator* iter); // pointer to dynamically created iterator. + virtual void init(); + virtual bool hasMoreLines() const; + virtual string nextLine(); + virtual string beautify(const string &line); + void setTabIndentation(int length = 4, bool forceTabs = false); + void setSpaceIndentation(int length = 4); + void setMaxInStatementIndentLength(int max); + void setMinConditionalIndentLength(int min); + void setClassIndent(bool state); + void setSwitchIndent(bool state); + void setCaseIndent(bool state); + void setBracketIndent(bool state); + void setBlockIndent(bool state); + void setNamespaceIndent(bool state); + void setLabelIndent(bool state); + void setCStyle(); + void setJavaStyle(); + void setEmptyLineFill(bool state); + void setPreprocessorIndent(bool state); + + + protected: + int getNextProgramCharDistance(const string &line, int i); + bool isLegalNameChar(char ch) const; + bool isWhiteSpace(char ch) const; + const string *findHeader(const string &line, int i, + const vector &possibleHeaders, + bool checkBoundry = true); + string trim(const string &str); + int indexOf(vector &container, const string *element); + + private: + ASBeautifier(const ASBeautifier ©); + void operator=(ASBeautifier&); // not to be implemented + + void initStatic(); + void registerInStatementIndent(const string &line, int i, int spaceTabCount, + int minIndent, bool updateParenStack); + string preLineWS(int spaceTabCount, int tabCount); + + static vector headers; + static vector nonParenHeaders; + static vector preprocessorHeaders; + static vector preBlockStatements; + static vector assignmentOperators; + static vector nonAssignmentOperators; + + static bool calledInitStatic; + + ASSourceIterator *sourceIterator; + vector *waitingBeautifierStack; + vector *activeBeautifierStack; + vector *waitingBeautifierStackLengthStack; + vector *activeBeautifierStackLengthStack; + vector *headerStack; + vector< vector* > *tempStacks; + vector *blockParenDepthStack; + vector *blockStatementStack; + vector *parenStatementStack; + vector *inStatementIndentStack; + vector *inStatementIndentStackSizeStack; + vector *parenIndentStack; + vector *bracketBlockStateStack; + string indentString; + const string *currentHeader; + const string *previousLastLineHeader; + const string *immediatelyPreviousAssignmentOp; + const string *probationHeader; + bool isInQuote; + bool isInComment; + bool isInCase; + bool isInQuestion; + bool isInStatement; + bool isInHeader; + bool isCStyle; + bool isInOperator; + bool isInTemplate; + bool isInConst; + bool isInDefine; + bool isInDefineDefinition; + bool classIndent; + bool isInClassHeader; + bool isInClassHeaderTab; + bool switchIndent; + bool caseIndent; + bool namespaceIndent; + bool bracketIndent; + bool blockIndent; + bool labelIndent; + bool preprocessorIndent; + bool isInConditional; + bool isMinimalConditinalIndentSet; + bool shouldForceTabIndentation; + int minConditionalIndent; + int parenDepth; + int indentLength; + int blockTabCount; + unsigned int leadingWhiteSpaces; + int maxInStatementIndent; + int templateDepth; + char quoteChar; + char prevNonSpaceCh; + char currentNonSpaceCh; + char currentNonLegalCh; + char prevNonLegalCh; + int prevFinalLineSpaceTabCount; + int prevFinalLineTabCount; + bool emptyLineFill; + bool backslashEndsPrevLine; + int defineTabCount; + }; +} + +#endif +/* + * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved. + * + * ASFormatter.cpp + * by Tal Davidson (davidsont@bigfoot.com) + * This file is a part of "Artistic Style" - an indentater and reformatter + * of C, C++, C# and Java source files. + * + * The "Artistic Style" project, including all files needed to compile it, + * is free software; you can redistribute it and/or use 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. + * + * 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. + * + * You should have received a copy of the GNU General Public + * License along with this program. + * + * + * Patches: + * 26 November 1998 - Richard Bullington - + * A correction of line-breaking in headers following '}', + + * was created using a variation of a patch by Richard Bullington. + * 08 May 2004 + * applied ASFormatter450670.patch.bz2, ASFormatter.cpp.patch.bz2, + * patch1_ssvb_patch.tar.gz + */ + +#include "compiler_defines.h" +#include "ASFormatter.h" + + +#include +#include +#include +#include +#include + + +#define INIT_CONTAINER(container, value) {if ( (container) != NULL ) delete (container); (container) = (value); } +#define DELETE_CONTAINER(container) {if ( (container) != NULL ) delete (container) ; } +#define IS_A(a,b) ( ((a) & (b)) == (b)) +#ifdef USES_NAMESPACE +using namespace std; + diff --git a/tests/examplefiles/example.lua b/tests/examplefiles/example.lua new file mode 100644 index 0000000..0289e58 --- /dev/null +++ b/tests/examplefiles/example.lua @@ -0,0 +1,250 @@ +--[[ + Auctioneer Advanced + Version: <%version%> (<%codename%>) + Revision: $Id: CoreMain.lua 2233 2007-09-25 03:57:33Z norganna $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that adds statistical history to the auction data that is collected + when the auction is scanned, so that you can easily determine what price + you will be able to sell an item for at auction or at a vendor whenever you + mouse-over an item in the game + + License: + 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program(see GPL.txt); if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Note: + This AddOn's source code is specifically designed to work with + World of Warcraft's interpreted AddOn system. + You have an implicit licence to use this AddOn with these facilities + since that is its designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] + + +--[[ + See CoreAPI.lua for a description of the modules API +]] + +if (not AucAdvanced) then AucAdvanced = {} end +if (not AucAdvancedData) then AucAdvancedData = {} end +if (not AucAdvancedLocal) then AucAdvancedLocal = {} end +if (not AucAdvancedConfig) then AucAdvancedConfig = {} end + +AucAdvanced.Version="<%version%>"; +if (AucAdvanced.Version == "<".."%version%>") then + AucAdvanced.Version = "5.0.DEV"; +end + +local private = {} + +-- For our modular stats system, each stats engine should add their +-- subclass to AucAdvanced.Modules.. and store their data into their own +-- data table in AucAdvancedData.Stats. +if (not AucAdvanced.Modules) then AucAdvanced.Modules = {Stat={},Util={},Filter={}} end +if (not AucAdvancedData.Stats) then AucAdvancedData.Stats = {} end +if (not AucAdvancedLocal.Stats) then AucAdvancedLocal.Stats = {} end + +function private.TooltipHook(vars, ret, frame, name, hyperlink, quality, quantity, cost, additional) + if EnhTooltip.LinkType(hyperlink) ~= "item" then + return -- Auctioneer hooks into item tooltips only + end + + -- Check to see if we need to force load scandata + local getter = AucAdvanced.Settings.GetSetting + if (getter("scandata.tooltip.display") and getter("scandata.force")) then + AucAdvanced.Scan.GetImage() + end + + for system, systemMods in pairs(AucAdvanced.Modules) do + for engine, engineLib in pairs(systemMods) do + if (engineLib.Processor) then engineLib.Processor("tooltip", frame, name, hyperlink, quality, quantity, cost, additional) end + end + end +end + +function private.HookAH() + hooksecurefunc("AuctionFrameBrowse_Update", AucAdvanced.API.ListUpdate) + for system, systemMods in pairs(AucAdvanced.Modules) do + for engine, engineLib in pairs(systemMods) do + if (engineLib.Processor) then + engineLib.Processor("auctionui") + end + end + end +end + +function private.OnLoad(addon) + addon = addon:lower() + + -- Check if the actual addon itself is loading + if (addon == "auc-advanced") then + Stubby.RegisterAddOnHook("Blizzard_AuctionUi", "Auc-Advanced", private.HookAH) + Stubby.RegisterFunctionHook("EnhTooltip.AddTooltip", 600, private.TooltipHook) + for pos, module in ipairs(AucAdvanced.EmbeddedModules) do + -- These embedded modules have also just been loaded + private.OnLoad(module) + end + end + + -- Notify the actual module if it exists + local auc, sys, eng = strsplit("-", addon) + if (auc == "auc" and sys and eng) then + for system, systemMods in pairs(AucAdvanced.Modules) do + if (sys == system:lower()) then + for engine, engineLib in pairs(systemMods) do + if (eng == engine:lower() and engineLib.OnLoad) then + engineLib.OnLoad(addon) + end + end + end + end + end + + -- Check all modules' load triggers and pass event to processors + for system, systemMods in pairs(AucAdvanced.Modules) do + for engine, engineLib in pairs(systemMods) do + if (engineLib.LoadTriggers and engineLib.LoadTriggers[addon]) then + if (engineLib.OnLoad) then + engineLib.OnLoad(addon) + end + end + if (engineLib.Processor and auc == "auc" and sys and eng) then + engineLib.Processor("load", addon) + end + end + end +end + +function private.OnUnload() + for system, systemMods in pairs(AucAdvanced.Modules) do + for engine, engineLib in pairs(systemMods) do + if (engineLib.OnUnload) then + engineLib.OnUnload() + end + end + end +end + +private.Schedule = {} +function private.OnEvent(...) + local event, arg = select(2, ...) + if (event == "ADDON_LOADED") then + local addon = string.lower(arg) + if (addon:sub(1,4) == "auc-") then + private.OnLoad(addon) + end + elseif (event == "AUCTION_HOUSE_SHOW") then + -- Do Nothing for now + elseif (event == "AUCTION_HOUSE_CLOSED") then + AucAdvanced.Scan.Interrupt() + elseif (event == "PLAYER_LOGOUT") then + AucAdvanced.Scan.Commit(true) + private.OnUnload() + elseif event == "UNIT_INVENTORY_CHANGED" + or event == "ITEM_LOCK_CHANGED" + or event == "CURSOR_UPDATE" + or event == "BAG_UPDATE" + then + private.Schedule["inventory"] = GetTime() + 0.15 + end +end + +function private.OnUpdate(...) + if event == "inventory" then + AucAdvanced.Post.AlertBagsChanged() + end + + local now = GetTime() + for event, time in pairs(private.Schedule) do + if time > now then + for system, systemMods in pairs(AucAdvanced.Modules) do + for engine, engineLib in pairs(systemMods) do + if engineLib.Processor then + engineLib.Processor(event, time) + end + end + end + end + private.Schedule[event] = nil + end +end + +private.Frame = CreateFrame("Frame") +private.Frame:RegisterEvent("ADDON_LOADED") +private.Frame:RegisterEvent("AUCTION_HOUSE_SHOW") +private.Frame:RegisterEvent("AUCTION_HOUSE_CLOSED") +private.Frame:RegisterEvent("UNIT_INVENTORY_CHANGED") +private.Frame:RegisterEvent("ITEM_LOCK_CHANGED") +private.Frame:RegisterEvent("CURSOR_UPDATE") +private.Frame:RegisterEvent("BAG_UPDATE") +private.Frame:RegisterEvent("PLAYER_LOGOUT") +private.Frame:SetScript("OnEvent", private.OnEvent) +private.Frame:SetScript("OnUpdate", private.OnUpdate) + +-- Auctioneer's debug functions +AucAdvanced.Debug = {} +local addonName = "Auctioneer" -- the addon's name as it will be displayed in + -- the debug messages +------------------------------------------------------------------------------- +-- Prints the specified message to nLog. +-- +-- syntax: +-- errorCode, message = debugPrint([message][, category][, title][, errorCode][, level]) +-- +-- parameters: +-- message - (string) the error message +-- nil, no error message specified +-- category - (string) the category of the debug message +-- nil, no category specified +-- title - (string) the title for the debug message +-- nil, no title specified +-- errorCode - (number) the error code +-- nil, no error code specified +-- level - (string) nLog message level +-- Any nLog.levels string is valid. +-- nil, no level specified +-- +-- returns: +-- errorCode - (number) errorCode, if one is specified +-- nil, otherwise +-- message - (string) message, if one is specified +-- nil, otherwise +------------------------------------------------------------------------------- +function AucAdvanced.Debug.DebugPrint(message, category, title, errorCode, level) + return DebugLib.DebugPrint(addonName, message, category, title, errorCode, level) +end + +------------------------------------------------------------------------------- +-- Used to make sure that conditions are met within functions. +-- If test is false, the error message will be written to nLog and the user's +-- default chat channel. +-- +-- syntax: +-- assertion = assert(test, message) +-- +-- parameters: +-- test - (any) false/nil, if the assertion failed +-- anything else, otherwise +-- message - (string) the message which will be output to the user +-- +-- returns: +-- assertion - (boolean) true, if the test passed +-- false, otherwise +------------------------------------------------------------------------------- +function AucAdvanced.Debug.Assert(test, message) + return DebugLib.Assert(addonName, test, message) +end + + diff --git a/tests/examplefiles/example.moo b/tests/examplefiles/example.moo new file mode 100644 index 0000000..1a15914 --- /dev/null +++ b/tests/examplefiles/example.moo @@ -0,0 +1,26 @@ +if (this.running) + player:tell("[Train] Error: already a jump in progress"); + return; +endif +this.running = 1; +this.aborted = 0; +this:announce_all("[Train] departure in 20 seconds"); +dest = this.targets[random(length(this.targets))]; +this:announce_all("[Train] Next stop is '", dest:title(), "'"); +this:announce_all("You hear the engines starting up"); +this.location:announce("The MOOTrain starts up his engines"); +suspend(20); +if (this.aborted) + this.running = 0; + this.aborted = 0; + return; +endif +this:announce_all("[Train] Departure!"); +this.location:announce_all("The MOOTrain leaves into the 42th dimension!"); +this:announce_all("Outside you see the lights of the 42th dimension"); +this:moveto(dest); +suspend(4); +this:announce_all("The glowing gets less, until you can see the clear shape of the room, the MOOTrain has landed in"); +this.location:announce_all("The MOOTrain arrives out of the 42th dimension!"); +this:announce_all("[Train] arrived in '", dest:title(), "'"); +this.running = 0; \ No newline at end of file diff --git a/tests/examplefiles/example.pas b/tests/examplefiles/example.pas new file mode 100644 index 0000000..ab11ee6 --- /dev/null +++ b/tests/examplefiles/example.pas @@ -0,0 +1,2708 @@ +// vim:ft=pascal + +unit YTools; + +{=============================================================================== + + cYcnus.YTools 1.0.3 Beta for Delphi 4+ + by licenser and Murphy + + ©2000-2003 by cYcnus + visit www.cYcnus.de + + licenser@cYcnus.de (Heinz N. Gies) + murphy@cYcnus.de (Kornelius Kalnbach) + + this unit is published under the terms of the GPL + +===============================================================================} + +interface + +uses + Windows, SysUtils, Classes, YTypes; + +const + BackSpace = #8; + Tab = #9; + LF = #10; //Line Feed + CR = #13; //Carriage Return + Space = #32; + EOLChars = [CR, LF]; +{$IFNDEF VER140} + sLineBreak = #13#10; + SwitchChars = ['/', '-']; +{$ENDIF} + EOL = sLineBreak; + MaxCard = High(Cardinal); + AllChars = [#0..#255]; + Alphabetical = ['A'..'Z', 'a'..'z']; + DecimalChars = ['0'..'9']; + AlphaNumerical = Alphabetical + DecimalChars; + StrangeChars = [#0..#31, #127, #129, #141..#144, #157, #158]; + + HexadecimalChars = DecimalChars + ['A'..'F', 'a'..'f']; + OctalChars = ['0'..'7']; + BinaryChars = ['0', '1']; + + QuoteChars = ['''', '"']; + WildCards = ['*', '?']; + FileNameEnemies = WildCards + ['\', '/', ':', '<', '>', '|']; + + HexChar: array[THex] of Char = ( + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); + LowerHexChar: array[THex] of Char = ( + '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'); + BaseNChar: array[TBaseN] of Char = ( + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H', + 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'); + + cYcnusOverlayColor = $050001; + + faFindEveryFile = faReadOnly + faHidden + faSysFile + faArchive; + + platWin9x = [VER_PLATFORM_WIN32s, VER_PLATFORM_WIN32_WINDOWS]; + + +{ Debugging } +procedure ClearReport(const ReportName: string); +procedure Report(const ReportName, Text: string); +procedure ReportFmt(const ReportName, Fmt: string; const Args: array of const); + +{ Params } +procedure GetParams(Strings: TStrings); overload; +function GetParams(const Separator: string = ' '): string; overload; + +function ParamNum(const S: string): Integer; +function ParamPrefixNum(const Prefix: string): Integer; +function Param(const S: string): Boolean; +function ParamPrefix(const Prefix: string): Boolean; + +function Switch(const Switch: string; const PrefixChars: TCharSet = SwitchChars; + IgnoreCase: Boolean = True): Boolean; +function GetParam(const Prefix: string = ''; const Default: string = ''): string; + +{ Dirs & UserName} +function GetMyDir(FullPath: Boolean = False): string; +function WinDir: string; +function SysDir: string; +function UserName: string; + +{ Strings & Chars} +function FirstChar(const S: string): Char; +function LastChar(const S: string): Char; + +function CharPos(C: Char; const S: string; Offset: Integer = 1): Integer; overload; +function CharPos(C: TCharSet; const S: string; Offset: Integer = 1): Integer; overload; +function CharPosR(C: Char; const S: string; Offset: Integer = -1): Integer; +function PosEx(const SubStr, S: string; Offset: Integer = 1): Integer; +function PosExText(const SubStr, S: string; Offset: Integer = 1): Integer; +function PosExAnsiText(const SubStr, S: string; Offset: Integer = 1): Integer; + +function UntilChar(const S: string; Brake: Char): string; overload; +function UntilChar(const S: string; Brake: TCharSet): string; overload; +function UntilLastChar(const S: string; Brake: Char; + IgnoreNoBrake: Boolean = True): string; + +function FromChar(const S: string; Brake: Char): string; overload; +function FromChar(const S: string; Brake: TCharSet): string; overload; +function FromLastChar(const S: string; Brake: Char; + IgnoreNoBrake: Boolean = False): string; + +function BetweenChars(const S: string; Start, Finish: Char; + Inclusive: Boolean = False): string; + +function UntilStr(const S: string; Brake: string): string; +function FromStr(const S: string; Brake: string): string; + +function StringWrap(const S: string; Width: Integer; const LineEnd: string = EOL): string; + +{ Splitting & Combining } +function Split(const S, Separator: string; IgnoreMultiSep: Boolean = True; + MinCount: Integer = 0): TStrA; overload; +procedure Split(const S, Separator: string; Strings: TStrings; + IgnoreMultiSep: Boolean = True); overload; +function Split(const S: string; Separators: TCharSet; + IgnoreMultiSep: Boolean = True; MinCount: Integer = 0): TStrA; overload; + +procedure TileStr(const S: string; BrakeStart: Integer; BrakeEnd: Integer; + out Left, Right: string); + +function Join(Strings: TStrings; Separator: string = ' '): string; overload; +function Join(StrA: TStrA; Separator: string = ' '): string; overload; + +function MulStr(const S: string; Count: Integer): string; + +{ Strings ausrichten } +function AlignR(const S: string; Width: Integer; Filler: Char = ' '): string; +function MaxStr(const S: string; MaxLen: Integer): string; + +{ Stringing } +function TrimAll(const S: string): string; + +function ControlChar(C: Char): Boolean; +function FriendlyChar(C: Char): Char; + +function FriendlyStr(const S: string): string; overload; +function FriendlyStr(a: TByteA): string; overload; + +function Quote(const S: string; Quoter: Char = '"'): string; +function UnQuote(const S: string): string; +function DeQuote(const S: string): string; + +function StrNumerus(const Value: Integer; const Singular, Plural: string; + const Zero: string = '0'): string; + +function MakeStr(const Items: array of const; Separator: string = ''): string; +procedure ShowText(const Items: array of const; Separator: string = ''); + +{ Delete } +function DeleteChars(const S: string; C: Char): string; overload; +function DeleteChars(const S: string; C: TCharSet): string; overload; +function ExtractChars(const S: string; C: TCharSet): string; + +{ Find } +function CharCount(const S: string; C: Char): Integer; + +function CharIn(const S: string; C: Char): Boolean; overload; +function CharIn(const S: string; C: TCharSet): Boolean; overload; + +function StrAtPos(const S: string; Pos: Integer; const Str: string): Boolean; +function StrAtBegin(const S, Str: string): Boolean; +function StrIn(const S, SubStr: string): Boolean; overload; +function StrIn(A: TStrA; const S: string): Boolean; overload; +function StrIn(SL: TStrings; const S: string): Boolean; overload; +function StrIndex(A: TStrA; const S: string): Integer; overload; +function StrIndex(SL: TStrings; const S: string): Integer; overload; + +function TextAtPos(const S: string; Pos: Integer; const Text: string): Boolean; +function TextAtBegin(const S, Text: string): Boolean; +function TextIn(const S, Text: string): Boolean; overload; +function TextIn(A: TStrA; const Text: string): Boolean; overload; +function TextIn(SL: TStrings; const Text: string): Boolean; overload; +function TextIndex(A: TStrA; const Text: string): Integer; overload; +function TextIndex(SL: TStrings; const Text: string): Integer; overload; + +{ Replace } +function ReplaceChars(const S: string; Old, New: Char): string; overload; +function ReplaceChars(const S: string; Old: TCharSet; New: Char): string; overload; + +function Replace(const S, Old, New: string): string; + +{ TStrings } +function SLOfFile(const FileName: string): TStringList; +function ContainsEmptyLines(SL: TStrings): Boolean; +procedure DeleteEmptyLines(SL: TStrings); +procedure DeleteCommentLines(SL: TStrings; const CommentSign: string = '//'); +procedure WriteSL(Strings: TStrings; const Prefix: string = ''; + const Suffix: string = ''); + +function FindLine(SL: TStrings; const S: string): Integer; + +procedure QuickSortSL(SL: TStringList); + +{ TStrA } +function IncStrA(StrA: TStrA): Integer; + +{ TByteA } +function StrOfByteA(a: TByteA): string; +function ByteAOfStr(const S: string): TByteA; +function ByteAOfInt(i: Integer): TByteA; +function IntOfByteA(A: TByteA): Integer; +function ByteAOfHex(const Hex: string): TByteA; + +function SameByteA(const A, B: TByteA): Boolean; +function Reverse(a: TByteA): TByteA; +function SaveByteA(Data: TByteA; const FileName: string; Overwrite: Boolean = True): Boolean; +function LoadByteA(const FileName: string): TByteA; + +function Endian(i: Integer): Integer; + +{ Files } +function SizeOfFile(const FileName: string): Integer; +function FileEx(const FileName: string; AllowFolders: Boolean = False): Boolean; +function LWPSolve(const Dir: string): string; +function LWPSlash(const Dir: string): string; + +function ExtractDrive(const FileName: string): string; +function ExtractPath(const FileName: string): string; +function ExtractPrefix(const FileName: string): string; +function ExtractSuffix(const FileName: string): string; + +function IsValidFileName(const FileName: string): Boolean; +function MakeValidFileName(FileName: string; const Default: string = 'File'): string; + +{ Converting } +function IsValidInteger(const S: string): Boolean; +function IsValidCardinal(const S: string): Boolean; + +function StrOfBool(flag: Boolean; const TrueStr: string = 'True'; + const FalseStr: string = 'False'): string; +function StrOfInt(i: Integer): string; +function CardOfStr(const S: string): Cardinal; + +function HexOrd(Hex: Char): THex; +function ByteOfHex(Hex: THexByteStr): Byte; + +function DecOfHex(const Hex: string): string; +function HexOfByte(b: Byte): THexByteStr; +function HexOfCard(i: Cardinal): string; overload; +function HexOfCard(i: Cardinal; Digits: Integer): string; overload; + +function PascalHexArray(a: TByteA; Name: string): string; + +function HexOfByteA(a: TByteA; Blocks: Integer = 1; + const Splitter: string = ' '): string; +function BinOfByteA(a: TByteA; Blocks: Integer = 4; + const Splitter: string = ' '): string; + +function CardOfHex(Hex: string): Cardinal; +function IntOfBin(Bin: string): Cardinal; + +function BinOfIntFill(n: cardinal; MinCount: Integer = 8): string; +function BinOfInt(n: cardinal): string; + +function BaseNOfInt(I: Cardinal; B: TBaseN): string; +function IntOfBaseN(V: string; B: TBaseN): Cardinal; + +{ Ranges } +function KeepIn(i, Bottom, Top: Variant): Variant; +function InRange(Value, Bottom, Top: Variant): Boolean; +function InStrictRange(Value, Bottom, Top: Variant): Boolean; +function Min(const A, B: Integer): Integer; overload; +function Min(const A: TIntA): Integer; overload; +function Max(const A, B: Integer): Integer; overload; +function Max(const A: TIntA): Integer; overload; + +const + RangesSeparator = ','; + RangeInnerSeparator = '-'; + RangeInfinite = '*'; + RangeSpecialChars = [RangesSeparator, RangeInnerSeparator, RangeInfinite]; + +function RangesOfStr(const S: string): TRanges; +function InRanges(Ranges: TRanges; TestValue: Cardinal): Boolean; + +function Success(Res: Integer; ResultOnSuccess: Integer = ERROR_SUCCESS): Boolean; +function Failure(Res: Integer; ResultOnSuccess: Integer = ERROR_SUCCESS): Boolean; + +function ExpandString(const S: string): string; + +{ Files } +procedure DeleteFiles(const Mask: string; ScanSubDirs: Boolean = True; + Attributes: Integer = faFindEveryFile); +procedure FileNew(const FileName: string); +function DateTimeOfFileTime(const FileTime: TFileTime): TDateTime; + +{ FileNames } +function GetFileNew(FileName: string; NoFloppyDrives: Boolean = True): string; + +{ Finding Files } +function FindAll(Strings: TStrings; const Mask: string; + ScanSubDirs: Boolean = True; Attributes: Integer = faFindEveryFile; + FileReturn: TFileNameFunc = nil): Boolean; +function FindAllFirst(const Mask: string; ScanSubDirs: Boolean = True; + Attributes: Integer = faFindEveryFile): string; + +function FullOSInfo: string; +function Win32PlatformStr: string; +function Win9x: Boolean; +function WinNT: Boolean; +function Win2000: Boolean; +function WinXP: Boolean; + +var + MyDir: string = ''; + LastSuccessRes: Integer = 0; + +{ Backward compatibility } +{$IFNDEF VER130} +function SameText(const S1, S2: string): Boolean; +{$ENDIF} + +implementation +{$IFNDEF VER140} +uses FileCtrl; +{$ENDIF} + +{$IFNDEF VER130} +function SameText(const S1, S2: string): Boolean; +begin + Result := CompareText(S1, S2) = 0; +end; +{$ENDIF} + +procedure Report(const ReportName, Text: string); +var + F: TextFile; + FileName: string; +begin + FileName := MyDir + ReportName + '.rep'; + Assign(F, FileName); + try + if not FileExists(FileName) then + Rewrite(F) + else + Append(F); + WriteLn(F, Text); + finally + Close(F); + end; +end; + +procedure ClearReport(const ReportName: string); +var + FileName: string; +begin + FileName := MyDir + ReportName + '.rep'; + DeleteFile(FileName); +end; + +procedure ReportFmt(const ReportName, Fmt: string; const Args: array of const); +begin + Report(ReportName, Format(Fmt, Args)); +end; + +procedure GetParams(Strings: TStrings); +var + P: PChar; + Param: string; + + function GetParamStr(var P: PChar; var Param: string): Boolean; + var + Quoted: Boolean; + begin + Param := ''; + + repeat + while (P[0] <> #0) and (P[0] <= ' ') do + Inc(P); + + Quoted := False; + while P[0] <> #0 do begin + if P[0] = '"' then begin + Quoted := not Quoted; + Inc(P); + Continue; end; + if (P[0] <= ' ') and not Quoted then + Break; + Param := Param + P[0]; + Inc(P); + end; + until (Param <> '') or (P[0] = #0); + + Result := Param <> ''; + end; + +begin + Strings.Clear; + P := GetCommandLine; + GetParamStr(P, Param); + while GetParamStr(P, Param) do + Strings.Add(Param); +end; + +function GetParams(const Separator: string = ' '): string; +var + SL: TStringList; +begin + SL := TStringList.Create; + GetParams(SL); + Result := Join(SL, Separator); + SL.Free; +end; + +function Switch(const Switch: string; const PrefixChars: TCharSet = SwitchChars; + IgnoreCase: Boolean = True): Boolean; +//= SysUtils.FindCmdLineSwitch +var + i: Integer; + s: string; +begin + Result := True; + + for i := 1 to ParamCount do begin + s := ParamStr(i); + + if (s <> '') and (s[1] in PrefixChars) then begin + //i know that always s <> '', but this is saver + s := Copy(s, 2, MaxInt); + if (s = Switch) or (IgnoreCase and (0=AnsiCompareText(s, Switch))) then + Exit; + end; + end; + + Result := False; +end; + +function ParamNum(const S: string): Integer; +begin + for Result := 1 to ParamCount do + if 0=AnsiCompareText(ParamStr(Result), S) then + Exit; + + Result := 0; +end; + +function ParamPrefixNum(const Prefix: string): Integer; +var + Len: Integer; +begin + Len := Length(Prefix); + for Result := 1 to ParamCount do + if 0=AnsiCompareText(Copy(ParamStr(Result), 1, Len), Prefix) then + Exit; + + Result := 0; +end; + +function Param(const S: string): Boolean; +begin + Result := ParamNum(S) > 0; +end; + +function ParamPrefix(const Prefix: string): Boolean; +begin + Result := ParamPrefixNum(Prefix) > 0; +end; + +function GetParam(const Prefix: string = ''; const Default: string = ''): string; +var + i: Integer; +begin + Result := Default; + + if Prefix = '' then begin + Result := ParamStr(1); + Exit; end; + + i := ParamPrefixNum(Prefix); + if i > 0 then + Result := Copy(ParamStr(i), Length(Prefix) + 1, MaxInt); +end; + +function GetMyDir(FullPath: Boolean = False): string; +var + Buffer: array[0..260] of Char; +begin + Result := ''; + SetString(Result, Buffer, GetModuleFileName(0, Buffer, SizeOf(Buffer))); + if FullPath then + Result := GetFileNew(Result); + Result := ExtractPath(Result); +end; + +function WinDir: string; +var + Res: PChar; +begin + Result := '\'; + GetMem(Res, MAX_PATH); + GetWindowsDirectory(Res, MAX_PATH); + Result := Res + '\'; + FreeMem(Res, MAX_PATH); +end; + +function SysDir: string; +var + Res: PChar; +begin + Result := '\'; + GetMem(Res, MAX_PATH); + GetSystemDirectory(Res, MAX_PATH); + Result := Res + '\'; + FreeMem(Res, MAX_PATH); +end; + +function UserName: string; +var + Len: Cardinal; + Res: PChar; +begin + Result := ''; + GetMem(Res, MAX_PATH); + Len := MAX_PATH; + GetUserName(Res, Len); + Result := Res; + FreeMem(Res, MAX_PATH); +end; + +function FirstChar(const S: string): Char; +begin + if s = '' then + Result := #0 + else + Result := s[1]; +end; + +function LastChar(const S: string): Char; +begin + if s = '' then + Result := #0 + else + Result := s[Length(s)]; +end; + +function CharPos(C: Char; const S: string; Offset: Integer = 1): Integer; +var + MaxPosToSearch: Integer; +begin + Result := Offset; + MaxPosToSearch := Length(S); + + while Result <= MaxPosToSearch do begin + if S[Result] = C then + Exit; + Inc(Result); + end; + + Result := 0; +end; + +function CharPos(C: TCharSet; const S: string; Offset: Integer = 1): Integer; +var + MaxPosToSearch: Integer; +begin + Result := Offset; + MaxPosToSearch := Length(S); + + while Result <= MaxPosToSearch do begin + if S[Result] in C then + Exit; + Inc(Result); + end; + + Result := 0; +end; + +function CharPosR(C: Char; const S: string; Offset: Integer = -1): Integer; +begin + if Offset < 0 then + Result := Length(S) + 1 - Offset + else + Result := Offset; + if Result > Length(S) then + Result := Length(S); + + while Result > 0 do begin + if S[Result] = C then + Exit; + Dec(Result); + end; +end; + +function PosEx(const SubStr, S: string; Offset: Integer = 1): Integer; +var + MaxPosToSearch, LenSubStr, i: Integer; +begin + if SubStr = '' then begin + Result := 0; + Exit; end; + + if Offset < 1 then + Result := 1 + else + Result := Offset; + + LenSubStr := Length(SubStr); + MaxPosToSearch := Length(S) - LenSubStr + 1; + + while Result <= MaxPosToSearch do begin + if S[Result] = SubStr[1] then begin + i := 1; + + while (i < LenSubStr) + and (S[Result + i] = SubStr[i + 1]) do + Inc(i); + + if i = LenSubStr then + Exit; + end; + Inc(Result); + end; + + Result := 0; +end; + +function PosExText(const SubStr, S: string; Offset: Integer = 1): Integer; +var + MaxPosToSearch, LenSubStr, i: Integer; + + function SameChar(a, b: Char): Boolean; + begin + Result := UpCase(a) = UpCase(b) + end; + +begin + if SubStr = '' then begin + Result := 0; + Exit; end; + + if Offset < 1 then + Result := 1 + else + Result := Offset; + + LenSubStr := Length(SubStr); + MaxPosToSearch := Length(S) - LenSubStr + 1; + + while Result <= MaxPosToSearch do begin + if SameChar(S[Result], SubStr[1]) then begin + i := 1; + + while (i < LenSubStr) + and (SameChar(S[Result + i], SubStr[i + 1])) do + Inc(i); + + if i = LenSubStr then + Exit; + end; + Inc(Result); + end; + + Result := 0; +end; + +function PosExAnsiText(const SubStr, S: string; Offset: Integer = 1): Integer; +var + MaxPosToSearch, LenSubStr, i: Integer; + + function SameChar(a, b: Char): Boolean; + begin + Result := CharLower(PChar(a)) = CharLower(PChar(b)); + end; + +begin + if SubStr = '' then begin + Result := 0; + Exit; end; + + if Offset < 1 then + Result := 1 + else + Result := Offset; + + LenSubStr := Length(SubStr); + MaxPosToSearch := Length(S) - LenSubStr + 1; + + while Result <= MaxPosToSearch do begin + if SameChar(S[Result], SubStr[1]) then begin + i := 1; + + while (i < LenSubStr) + and (SameChar(S[Result + i], SubStr[i + 1])) do + Inc(i); + + if i = LenSubStr then + Exit; + end; + Inc(Result); + end; + + Result := 0; +end; + +function UntilChar(const S: string; Brake: Char): string; +var + p: Integer; +begin + p := CharPos(Brake, S); + + if p > 0 then + Result := Copy(S, 1, p - 1) + else + Result := S; +end; + +function UntilChar(const S: string; Brake: TCharSet): string; +var + p: Integer; +begin + Result := ''; + p := CharPos(Brake, S); + + if p > 0 then + Result := Copy(S, 1, p - 1) + else + Result := S; +end; + +function UntilLastChar(const S: string; Brake: Char; + IgnoreNoBrake: Boolean = True): string; +var + p: Integer; +begin + Result := ''; + p := CharPosR(Brake, S); + + if p > 0 then + Result := Copy(S, 1, p - 1) + else if IgnoreNoBrake then + Result := S; +end; + +function FromChar(const S: string; Brake: Char): string; +var + p: Integer; +begin + Result := ''; + p := CharPos(Brake, S); + + if p > 0 then + Result := Copy(S, p + 1, Length(S) - p); +end; + +function FromChar(const S: string; Brake: TCharSet): string; +var + p: Integer; +begin + Result := ''; + p := CharPos(Brake, S); + + if p > 0 then + Result := Copy(S, p + 1, Length(S) - p); +end; + +function FromLastChar(const S: string; Brake: Char; + IgnoreNoBrake: Boolean = False): string; +var + p: Integer; +begin + Result := ''; + p := CharPosR(Brake, S); + + if p > 0 then + Result := Copy(S, p + 1, Length(S) - p) + else if IgnoreNoBrake then + Result := S; +end; + +function BetweenChars(const S: string; Start, Finish: Char; + Inclusive: Boolean = False): string; +var + p, fin: Integer; +begin + Result := ''; + + p := CharPos(Start, S); + if p = 0 then + Exit; + + fin := CharPos(Finish, S, p + 1); + if fin = 0 then + Exit; + + if not Inclusive then begin + Inc(p); + Dec(fin); + end; + + Result := Copy(S, p, fin - p + 1); +end; + +function UntilStr(const S: string; Brake: string): string; +var + p: Integer; +begin + if Length(Brake) = 1 then begin + Result := UntilChar(S, Brake[1]); + Exit; end; + + p := PosEx(Brake, S); + + if p > 0 then + Result := Copy(S, 1, p - 1) + else + Result := S; +end; + +function FromStr(const S: string; Brake: string): string; +var + p: Integer; +begin + if Length(Brake) = 1 then begin + Result := FromChar(S, Brake[1]); + Exit; end; + + Result := ''; + p := PosEx(Brake, s); + + if p > 0 then begin + Inc(p, Length(Brake)); + Result := Copy(S, p, Length(S) - p + 1); + end; +end; + +function StringWrap(const S: string; Width: Integer; const LineEnd: string = EOL): string; +var + i: Integer; +begin + Result := ''; + if (S = '') or (Width < 1) then + Exit; + + i := 1; + while True do begin + Result := Result + Copy(S, i, Width); + Inc(i, Width); + if i <= Length(S) then + Result := Result + LineEnd + else + Exit; + end; +end; + +function Split(const S, Separator: string; IgnoreMultiSep: Boolean = True; + MinCount: Integer = 0): TStrA; +var + p, fin, SepLen: Integer; + + procedure Add(const S: string); + begin + if IgnoreMultiSep and (S = '') then + Exit; + SetLength(Result, Length(Result) + 1); + Result[High(Result)] := S; + end; + +begin + if S = '' then begin + if Length(Result) < MinCount then + SetLength(Result, MinCount); + Exit; end; + + Result := nil; + SepLen := Length(Separator); + + p := 1; + fin := PosEx(Separator, S); + while fin > 0 do begin + Add(Copy(S, p, fin - p)); + p := fin + SepLen; + fin := PosEx(Separator, S, p); + end; + Add(Copy(S, p, Length(S) - p + 1)); + + if Length(Result) < MinCount then + SetLength(Result, MinCount); +end; + +procedure Split(const S, Separator: string; Strings: TStrings; + IgnoreMultiSep: Boolean = True); +var + p, fin, SepLen: Integer; + + procedure Add(const S: string); + begin + if IgnoreMultiSep and (S = '') then + Exit; + Strings.Add(S); + end; + +begin + if S = '' then + Exit; + + Strings.BeginUpdate; + SepLen := Length(Separator); + p := 1; + fin := PosEx(Separator, S); + while fin > 0 do begin + Add(Copy(S, p, fin - p)); + p := fin + SepLen; + fin := PosEx(Separator, S, p); + end; + Add(Copy(S, p, Length(S) - p + 1)); + Strings.EndUpdate; +end; + +function Split(const S: string; Separators: TCharSet; + IgnoreMultiSep: Boolean = True; MinCount: Integer = 0): TStrA; +var + p, fin: Integer; + + procedure Add(const S: string); + begin + if IgnoreMultiSep and (S = '') then + Exit; + SetLength(Result, Length(Result) + 1); + Result[High(Result)] := S; + end; + +begin + if S = '' then begin + if Length(Result) < MinCount then + SetLength(Result, MinCount); + Exit; end; + + Result := nil; + + p := 1; + fin := CharPos(Separators, S); + while fin > 0 do begin + Add(Copy(S, p, fin - p)); + p := fin + 1; + fin := CharPos(Separators, S, p); + end; + Add(Copy(S, p, Length(S) - p + 1)); + + if Length(Result) < MinCount then + SetLength(Result, MinCount); +end; + +procedure TileStr(const S: string; BrakeStart: Integer; BrakeEnd: Integer; + out Left, Right: string); +begin + Left := Copy(S, 1, BrakeStart-1); + Right := Copy(S, BrakeEnd + 1, MaxInt); +end; + +function Join(Strings: TStrings; Separator: string = ' '): string; +var + i, imax: Integer; +begin + Result := ''; + imax := Strings.Count-1; + for i := 0 to imax do begin + Result := Result + Strings[i]; + if i < imax then + Result := Result + Separator; + end; +end; + +function Join(StrA: TStrA; Separator: string = ' '): string; overload; +var + i: Integer; +begin + Result := ''; + for i := 0 to High(StrA) do begin + Result := Result + StrA[i]; + if i < High(StrA) then + Result := Result + Separator; + end; +end; + +function MulStr(const S: string; Count: Integer): string; +var + P: PChar; + Len, i: Integer; +begin + Result := ''; + if Count = 0 then + Exit; + + Len := Length(S); + SetLength(Result, Len * Count); + + P := Pointer(Result); + for i := 1 to Count do begin + Move(Pointer(S)^, P^, Len); + Inc(P, Len); + end; +end; + +function AlignR(const S: string; Width: Integer; Filler: Char = ' '): string; +begin + Result := MulStr(Filler, Width - Length(S)) + S; +end; + +function MaxStr(const S: string; MaxLen: Integer): string; +var + Len: Integer; +begin + Len := Length(S); + if Len <= MaxLen then begin + Result := S; + Exit end; + + Result := Copy(S, 1, MaxLen - 3) + '...'; +end; + +function TrimAll(const S: string): string; +var + i: Integer; +begin + for i := 1 to Length(S) do + if S[i] > #32 then + Result := Result + S[i]; +end; + +function ControlChar(C: Char): Boolean; +begin + Result := C in StrangeChars; +end; + +function FriendlyChar(C: Char): Char; +begin + case C of + #0: Result := '.'; + #1..#31: Result := '?'; + #255: Result := '#'; + else + Result := C; + end; +end; + +function FriendlyStr(const S: string): string; +var + i: Integer; +begin + SetLength(Result, Length(S)); + for i := 1 to Length(S) do + Result[i] := FriendlyChar(S[i]); +end; + +function FriendlyStr(a: TByteA): string; +var + i: Integer; +begin + SetLength(Result, Length(a)); + for i := 0 to High(a) do + Result[i + 1] := FriendlyChar(Char(a[i])); +end; + +function Quote(const S: string; Quoter: Char = '"'): string; +begin + Result := S; + + if FirstChar(S) <> Quoter then + Result := Quoter + Result; + + if LastChar(S) <> Quoter then + Result := Result + Quoter; +end; + +function DeQuote(const S: string): string; +begin + Result := ''; + if Length(S) > 2 then + Result := Copy(S, 2, Length(S) - 2); +end; + +function UnQuote(const S: string): string; +var + Start, Len: Integer; +begin + Start := 1; + Len := Length(S); + + if (S <> '') and (S[1] in ([#0..#32] + QuoteChars)) then begin + if (LastChar(S) = S[1]) then + Dec(Len); + Inc(Start); + end; + + Result := Copy(S, Start, Len - Start + 1); +end; + +function StrNumerus(const Value: Integer; const Singular, Plural: string; + const Zero: string = '0'): string; +begin + if Abs(Value) = 1 then + Result := IntToStr(Value) + ' ' + Singular + else if Value = 0 then + Result := Zero + ' ' + Plural + else + Result := IntToStr(Value) + ' ' + Plural; +end; + +function MakeStr(const Items: array of const; Separator: string = ''): string; +const + BoolStrings: array[Boolean] of string = ('False', 'True'); + +var + i: Integer; + + function StrOfP(P: Pointer): string; + begin + if P = nil then + Result := '[nil]' + else + Result := '[' + IntToStr(Cardinal(P)) + ']'; + end; + + procedure Add(const S: string); + begin + Result := Result + s + Separator; + end; + +begin + Result := ''; + for i := 0 to High(Items) do + with Items[i] do + case VType of + vtString: Add(VString^); + vtInteger: Add(IntToStr(VInteger)); + vtBoolean: Add(BoolStrings[VBoolean]); + vtChar: Add(VChar); + vtPChar: Add(VPChar); + vtExtended: Add(FloatToStr(VExtended^)); + vtObject: if VObject is TComponent then + Add(TComponent(VObject).Name) + else + Add(VObject.ClassName); + vtClass: Add(VClass.ClassName); + vtAnsiString: Add(string(VAnsiString)); + vtCurrency: Add(CurrToStr(VCurrency^)); + vtInt64: Add(IntToStr(VInt64^)); + vtVariant: Add(string(VVariant^)); + + vtWideChar: Add(VWideChar); + vtPWideChar: Add(VPWideChar); + vtInterface: Add(StrOfP(VInterface)); + vtPointer: Add(StrOfP(VPointer)); + vtWideString: Add(WideString(VWideString)); + end; + if Result <> '' then + SetLength(result, Length(Result) - Length(Separator)); +end; + +procedure ShowText(const Items: array of const; Separator: string = ''); +var + Text: string; +begin + Text := MakeStr(Items, Separator); + + MessageBox(0, PChar(Text), 'Info', MB_OK and MB_APPLMODAL); +end; + +function DeleteChars(const S: string; C: Char): string; +var + i: Integer; +begin + Result := ''; + for i := 1 to Length(S) do + if S[i] <> C then + Result := Result + S[i]; +end; + +function DeleteChars(const S: string; C: TCharSet): string; +var + i: Integer; +begin + Result := ''; + for i := 1 to Length(S) do + if not (S[i] in C) then + Result := Result + S[i]; +end; + +function ExtractChars(const S: string; C: TCharSet): string; +var + i: Integer; +begin + Result := ''; + for i := 1 to Length(S) do + if S[i] in C then + Result := Result + S[i]; +end; + +function CharCount(const S: string; C: Char): Integer; +var + i: Integer; +begin + Result := 0; + for i := 1 to Length(S) do + if S[i] = C then + Inc(Result); +end; + +function StrAtPos(const S: string; Pos: Integer; const Str: string): Boolean; +begin + Result := (Str <> '') and (Str = Copy(S, Pos, Length(Str))); +end; + +function TextAtPos(const S: string; Pos: Integer; const Text: string): Boolean; +begin + Result := (Text <> '') and SameText(Text, Copy(S, Pos, Length(Text))); +end; + +function StrAtBegin(const S, Str: string): Boolean; +begin + Result := StrAtPos(S, 1, Str); +end; + +function TextAtBegin(const S, Text: string): Boolean; +begin + Result := TextAtPos(S, 1, Text); +end; + +function CharIn(const S: string; C: Char): Boolean; +var + i: Integer; +begin + Result := True; + for i := 1 to Length(S) do + if S[i] = C then Exit; + Result := False; +end; + +function CharIn(const S: string; C: TCharSet): Boolean; +var + i: Integer; +begin + Result := False; + for i := 1 to Length(S) do begin + Result := S[i] in C; + if Result then + Exit; + end; +end; + +function StrIn(const S, SubStr: string): Boolean; +begin + Result := PosEx(SubStr, S) > 0; +end; + +function StrIn(SL: TStrings; const S: string): Boolean; +var + i: Integer; +begin + Result := False; + for i := 0 to SL.Count-1 do begin + Result := (S = SL[i]); + if Result then + Exit; + end; +end; + +function StrIn(A: TStrA; const S: string): Boolean; +var + i: Integer; +begin + Result := False; + for i := Low(A) to High(A) do begin + Result := (S = A[i]); + if Result then + Exit; + end; +end; + +function TextIn(const S, Text: string): Boolean; +begin + Result := PosExText(Text, S) > 0; +end; + +function TextIn(SL: TStrings; const Text: string): Boolean; +var + i: Integer; +begin + Result := False; + for i := 0 to SL.Count-1 do begin + Result := SameText(Text, SL[i]); + if Result then + Exit; + end; +end; + +function TextIn(A: TStrA; const Text: string): Boolean; +var + i: Integer; +begin + Result := False; + for i := Low(A) to High(A) do begin + Result := SameText(Text, A[i]); + if Result then + Exit; + end; +end; + +function StrIndex(SL: TStrings; const S: string): Integer; +begin + for Result := 0 to SL.Count-1 do + if S = SL[Result] then + Exit; + Result := -1; +end; + +function StrIndex(A: TStrA; const S: string): Integer; +begin + for Result := Low(A) to High(A) do + if S = A[Result] then + Exit; + Result := -1; +end; + +function TextIndex(SL: TStrings; const Text: string): Integer; +begin + for Result := 0 to SL.Count-1 do + if SameText(Text, SL[Result]) then + Exit; + Result := -1; +end; + +function TextIndex(A: TStrA; const Text: string): Integer; +begin + for Result := Low(A) to High(A) do + if SameText(Text, A[Result]) then + Exit; + Result := -1; +end; + +function ReplaceChars(const S: string; Old, New: Char): string; +var + i: Integer; +begin + Result := S; + for i := 1 to Length(Result) do + if Result[i] = Old then + Result[i] := New; +end; + +function ReplaceChars(const S: string; Old: TCharSet; New: Char): string; +var + i: Integer; +begin + Result := S; + for i := 1 to Length(Result) do + if Result[i] in Old then + Result[i] := New; +end; + +function Replace(const S, Old, New: string): string; +var + oldp, ps: Integer; +begin + ps := 1; + Result := ''; + while True do begin + oldp := ps; + ps := PosEx(Old, S, oldp); + if ps = 0 then begin + Result := Result + Copy(S, oldp, Length(S) - oldp + 1); + Exit; end; + Result := Result + Copy(S, oldp, ps - oldp) + New; + Inc(ps, Length(Old)); + end; +end; + +function SLOfFile(const FileName: string): TStringList; +begin + Result := TStringList.Create; + if FileExists(FileName) then + Result.LoadFromFile(FileName); +end; + +function ContainsEmptyLines(SL: TStrings): Boolean; +begin + Result := StrIn(SL, ''); +end; + +procedure DeleteEmptyLines(SL: TStrings); +var + i: Integer; +begin + i := 0; + while i < SL.Count do begin + if SL[i] = '' then + SL.Delete(i) + else + Inc(i); + end; +end; + +procedure DeleteCommentLines(SL: TStrings; const CommentSign: string = '//'); +var + i: Integer; +begin + i := 0; + while i < SL.Count do begin + if (SL[i] = '') or (StrAtBegin(TrimLeft(SL[i]), CommentSign)) then + SL.Delete(i) + else + Inc(i); + end; +end; + +function FindLine(SL: TStrings; const S: string): Integer; +begin + for Result := 0 to SL.Count-1 do + if TextAtBegin(SL[Result], S) then + Exit; + Result := -1; +end; + +procedure QuickSortSL(SL: TStringList); + + procedure Sort(l, r: Integer); + var + i,j: Integer; + z,x: string; + begin + i := l; + j := r; + x := SL[(j + i) div 2]; + repeat + while SL[i] < x do Inc(i); + while SL[j] > x do Dec(j); + if i <= j then begin + z := SL[i]; + SL[i] := SL[j]; + SL[j] := z; + Inc(i); Dec(j); + end; + until i > j; + if j > l then Sort(l, j); + if i < r then Sort(i, r); + end; + +begin + if SL.Count > 0 then + Sort(0, SL.Count-1); +end; + +function IncStrA(StrA: TStrA): Integer; +begin + SetLength(StrA, Length(StrA) + 1); + Result := High(StrA); +end; + +function StrOfByteA(a: TByteA): string; +begin + Result := string(Copy(a, 0, Length(a))); +end; + +function ByteAOfStr(const S: string): TByteA; +begin + Result := TByteA(Copy(S, 1, Length(s))); +end; + +function ByteAOfInt(i: Integer): TByteA; +begin + SetLength(Result, SizeOf(Integer)); + Move(i, Pointer(Result)^, SizeOf(Integer)); +end; + +function IntOfByteA(A: TByteA): Integer; +begin + Result := 0; + Move(Pointer(A)^, Result, Min(Length(A), SizeOf(Integer))); +end; + +function ByteAOfHex(const Hex: string): TByteA; +var + i: Integer; + h: string; +begin + h := ExtractChars(Hex, HexadecimalChars); + SetLength(Result, Length(h) div 2); + for i := 0 to High(Result) do + Result[i] := ByteOfHex(Copy(h, (i shl 1) + 1, 2)); +end; + +function SizeOfFile(const FileName: string): Integer; +var + F: file; +begin + AssignFile(F, FileName); + {$I-}Reset(F, 1);{$I+} + if IOResult = 0 then begin + Result := FileSize(F); + CloseFile(F); + end else + Result := 0; +end; + +function FileEx(const FileName: string; AllowFolders: Boolean = False): Boolean; +var + FindData: TWin32FindData; +begin + if FileName = '' then begin + Result := False; + Exit; end; + + Result := (AllowFolders and DirectoryExists(FileName)) or + (FindFirstFile(PChar(FileName), FindData) <> INVALID_HANDLE_VALUE); + Result := Result and not CharIn(FileName, WildCards); + Result := Result and (AllowFolders + or ((FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0)); +end; + +function LWPSolve(const Dir: string): string; +begin + if (Dir <> '') and (Dir[Length(Dir)] = '\') then begin + Result := Copy(Dir, 1, Length(Dir) - 1); + end else + Result := Dir; +end; + +function LWPSlash(const Dir: string): string; +begin + if (Dir <> '') and (Dir[Length(Dir)] = '\') then begin + Result := Copy(Dir, 1, Length(Dir)); + end else + Result := Dir + '\'; +end; + +function ExtractDrive(const FileName: string): string; +begin + Result := ''; + if (Length(FileName) >= 2) and (FileName[2] = ':') then + Result := UpperCase(FileName[1] + ':\'); +end; + +function ExtractPath(const FileName: string): string; +var + p: Integer; +begin + p := CharPosR('\', FileName); + if P > 0 then + Result := Copy(FileName, 1, p) + else + Result := FileName; +end; + +function ExtractPrefix(const FileName: string): string; +begin + Result := UntilLastChar(ExtractFileName(FileName), '.'); +end; + +function ExtractSuffix(const FileName: string): string; +begin + Result := FromLastChar(ExtractFileName(FileName), '.'); +end; + +function SameByteA(const A, B: TByteA): Boolean; +begin + Result := (A = B) or ((Length(A) = Length(B)) and CompareMem(A, B, Length(A))); +end; + +function Reverse(A: TByteA): TByteA; +var + i: Integer; +begin + SetLength(Result, Length(A)); + + for i := 0 to High(A) do + Result[High(Result) - i] := A[i]; +end; + +function Endian(i: Integer): Integer; +type + EndianArray = packed array[0..3] of Byte; +var + a, b: EndianArray; +begin + a := EndianArray(i); + b[0] := a[3]; + b[1] := a[2]; + b[2] := a[1]; + b[3] := a[0]; + Result := Integer(b); +end; + +function SaveByteA(Data: TByteA; const FileName: string; + Overwrite: Boolean = True): Boolean; +var + F: file; +begin + if FileExists(FileName) and not Overwrite then begin + Result := False; + Exit end; + + AssignFile(F, FileName); + {$I-}Rewrite(F, 1);{$I+} + if IOResult = 0 then begin + if Length(Data) > 0 then + BlockWrite(F, Data[0], Length(Data)); + CloseFile(F); + Result := True; + end else + Result := False; +end; + +function LoadByteA(const FileName: string): TByteA; +var + F: file; +begin + AssignFile(F, FileName); + {$I-}Reset(F, 1);{$I+} + if IOResult = 0 then begin + SetLength(Result, FileSize(F)); + if Length(Result) > 0 then + BlockRead(F, Result[0], FileSize(F)); + CloseFile(F); + end else + SetLength(Result, 0); +end; + +function IsValidFileName(const FileName: string): Boolean; +begin + Result := (FileName <> '') and not CharIn(FileName, FileNameEnemies) + and CharIn(Trim(FileName), AllChars - ['.']); +end; + +function MakeValidFileName(FileName: string; const Default: string = 'File'): string; +begin + if FileName = '' then + FileName := Default; + + if CharIn(FileName, FileNameEnemies) then + Result := ReplaceChars(FileName, FileNameEnemies, '_') + else if not CharIn(Trim(FileName), AllChars - ['.']) then + Result := Default + else + Result := FileName; +end; + +function IsValidInteger(const S: string): Boolean; +{const + LowInt = '2147483648'; + HighInt = '2147483647'; +var + len, RealLen, i, o: Integer; + c: Char; +begin + Result := False; + if S = '' then + Exit; + + len := Length(S); + o := 1; + + if S[1] = '-' then begin + if len = 1 then + Exit; + Inc(o); + while (o <= len) and (S[o] = '0') do + Inc(o); + if o > len then + Exit; + if o < len then begin + RealLen := len - o + 1; + if RealLen > Length(LowInt) then + Exit + else if RealLen = Length(LowInt) then begin + for i := 1 to Length(LowInt) do begin + c := S[i + o - 1]; + if (c < '0') or (c > LowInt[i]) then + Exit; + if c in ['0'..Char((Byte(LowInt[i])-1))] then + Break; + end; + Inc(o, i); + end; + end; + end else begin + while (o <= len) and (S[o] = '0') do + Inc(o); + if o <= len then begin + RealLen := len - o + 1; + if RealLen > Length(HighInt) then + Exit + else if RealLen = Length(HighInt) then begin + for i := 1 to Length(HighInt) do begin + c := S[i + o - 1]; + if (c < '0') or (c > HighInt[i]) then + Exit; + if c in ['0'..Char((Byte(HighInt[i])-1))] then + Break; + end; + Inc(o, i); + end; + end; + end; + + for i := o to len do + if not (S[i] in ['0'..'9']) then + Exit; + + Result := True; } +var + i: Int64; +begin + i := StrToInt64Def(S, High(Int64)); + Result := (i >= Low(Integer)) and (i <= High(Integer)); +end; + +function IsValidCardinal(const S: string): Boolean; +{const + HighCard = '4294967295'; +var + len, RealLen, i, o: Integer; +begin + Result := False; + if S = '' then + Exit; + + len := Length(S); + o := 1; + + while (o <= len) and (S[o] = '0') do + Inc(o); + if o <= len then begin + RealLen := len - o + 1; + if RealLen > Length(HighCard) then + Exit + else if RealLen = Length(HighCard) then begin + for i := 1 to Length(HighCard) do begin + if S[i + o - 1] > HighCard[i] then + Exit; + if S[i + o - 1] in ['0'..Char((Byte(HighCard[i])-1))] then + Break; + end; + Inc(o, i); + end; + end; + + for i := o to len do + if not (S[i] in ['0'..'9']) then + Exit; + + Result := True; } +var + i: Int64; +begin + i := StrToInt64Def(S, -1); + Result := (i >= 0) and (i <= High(Cardinal)); +end; + +function StrOfBool(flag: Boolean; const TrueStr: string = 'True'; + const FalseStr: string = 'False'): string; +begin + if Flag then + Result := TrueStr + else + Result := FalseStr; +end; + +function StrOfInt(i: Integer): string; +begin +{ if i = 0 then begin + Result := '0'; + Exit end; + + while i > 0 do begin + Result := Char(Byte('0') + (i mod 10)) + Result; + i := i div 10; + end;} + Result := IntToStr(i); +end; + +function CardOfStr(const S: string): Cardinal; +var + Res: Int64; +begin + Res := StrToInt64Def(S, -1); + if Res > High(Cardinal) then + Res := High(Cardinal) + else if Res < 0 then + Res := 0; + Result := Cardinal(Res); +end; + +function HexOrd(Hex: Char): THex; +begin + case Hex of + '0'..'9': + Result := Byte(Hex) - 48; + 'A'..'F': + Result := Byte(Hex) - 55; + 'a'..'f': + Result := Byte(Hex) - 87; + else + Result := 0; + end; +end; + +function ByteOfHex(Hex: THexByteStr): Byte; +begin + Result := (HexOrd(Hex[1]) shl 4) + HexOrd(Hex[2]); +end; + +function DecOfHex(const Hex: string): string; +begin + Result := IntToStr(CardOfHex(Hex)); +end; + +function HexOfByte(b: Byte): THexByteStr; +begin + Result := HexChar[(b and $F0) shr 4] + + HexChar[ b and $0F ]; +end; + +{function HexOfCard2(c: Cardinal): string; +var + Data: array[0..(1 shl 4) - 1] of Char; + i: Integer; +begin + for i := 0 to (1 shl 4) - 1 do + if i < 10 then + Data[i] := Char(Ord('0') + i) + else + Data[i] := Char(Ord('A') + i - 10); + + Result := Data[(c and (((1 shl (1 shl 2)) - 1) shl (7 shl 2))) shr (7 shl 2)] + + Data[(c and (((1 shl (1 shl 2)) - 1) shl (6 shl 2))) shr (6 shl 2)] + + Data[(c and (((1 shl (1 shl 2)) - 1) shl (5 shl 2))) shr (5 shl 2)] + + Data[(c and (((1 shl (1 shl 2)) - 1) shl (4 shl 2))) shr (4 shl 2)] + + Data[(c and (((1 shl (1 shl 2)) - 1) shl (3 shl 2))) shr (3 shl 2)] + + Data[(c and (((1 shl (1 shl 2)) - 1) shl (2 shl 2))) shr (2 shl 2)] + + Data[(c and (((1 shl (1 shl 2)) - 1) shl (1 shl 2))) shr (1 shl 2)] + + Data[(c and (((1 shl (1 shl 2)) - 1) shl (0 shl 2))) shr (0 shl 2)]; +end; } + +function HexOfCard(i: Cardinal): string; +var + a: Cardinal; +begin + Result := ''; + while i > 0 do begin + a := i and $F; + Result := HexChar[a] + Result; + i := i shr 4; + end; +end; + +function HexOfCard(i: Cardinal; Digits: Integer): string; +var + a: Cardinal; +begin + Result := ''; + while i > 0 do begin + a := i and $F; + Result := HexChar[a] + Result; + i := i shr 4; + end; + Result := MulStr('0', Digits - Length(Result)) + Result; +end; + +function PascalHexArray(a: TByteA; Name: string): string; +var + i, len: Integer; +begin + Result := 'const' + EOL + + ' ' + Name + ': array[0..' + IntToStr(High(a)) + '] of Byte = ('; + + len := Length(a); + for i := 0 to len-1 do begin + if (i mod 19) = 0 then + Result := Result + EOL + ' ' + ' '; + Result := Result + '$' + HexOfByte(a[i]); + if i < len-1 then + Result := Result + ','; + end; + Result := Result + EOL + ' );'; +end; + +function HexOfByteA(a: TByteA; Blocks: Integer = 1; + const Splitter: string = ' '): string; +var + i: Integer; +begin + Result := ''; + + if Blocks > 0 then + for i := 0 to High(a) do begin + Result := Result + HexOfByte(a[i]); + if i < High(a) then + if ((i+1) mod Blocks) = 0 then + Result := Result + Splitter; + end + else + for i := 0 to High(a) do + Result := Result + HexOfByte(a[i]); +end; + +function BinOfByteA(a: TByteA; Blocks: Integer = 4; + const Splitter: string = ' '): string; +var + i, max: Integer; + Bit: Boolean; +begin + Result := ''; + + if Blocks > 0 then begin + max := 8 * (High(a)) + 7; + for i := 0 to max do begin + Bit := 7-(i mod 8) in TBitSet(a[i div 8]); + Result := Result + Char(Byte('0') + Byte(Bit)); + if i < max then + if ((i+1) mod Blocks) = 0 then + Result := Result + Splitter; + end; + end else + for i := 0 to High(a) do + Result := Result + Char(Byte('0') + a[i] shr (i and 8)); +end; + +function CardOfHex(Hex: string): Cardinal; +var + i: Integer; +begin + Result := 0; + Hex := Copy(ExtractChars(Hex, HexadecimalChars), 1, 8); + + for i := 1 to Length(Hex) do + if Hex[i] <> '0' then + Inc(Result, HexOrd(Hex[i]) shl ((Length(Hex) - i) shl 2)); +end; + +function IntOfBin(Bin: string): Cardinal; +var + i: Integer; +begin + Result := 0; + Bin := Copy(ExtractChars(Bin, BinaryChars), 1, 32); + + for i := Length(Bin) downto 1 do + if Bin[i] = '1' then + Inc(Result, 1 shl (Length(Bin) - i)); +end; + +function BinOfInt(n: Cardinal): string; +var + a: Integer; +begin + if n = 0 then begin + Result := '0'; + exit; end; + + Result := ''; + while n > 0 do begin + a := n and 1; + Result := Char(a + Byte('0')) + Result; + n := n shr 1; + end; +end; + +function BinOfIntFill(n: Cardinal; MinCount: Integer = 8): string; +var + a: Integer; +begin + if n = 0 then begin + Result := MulStr('0', MinCount); + Exit; end; + + Result := ''; + while n > 0 do begin + a := n and 1; + Result := Char(a + Byte('0')) + Result; + n := n shr 1; + end; + Result := MulStr('0', MinCount - Length(Result)) + Result; +end; + +function BaseNOfInt(I: Cardinal; B: TBaseN): string; +var + a: Integer; +begin + if (B < 2) or (i = 0) then begin + Result := '0'; + Exit; end; + + Result := ''; + while i > 0 do begin + a := i mod B; + Result := BaseNChar[a] + Result; + i := i div B; + end; +end; + +function IntOfBaseN(V: string; B: TBaseN): Cardinal; +var + i: Integer; + F: Cardinal; + c: Byte; +begin + Result := 0; + V := TrimAll(V); + F := 1; + for i := Length(V) downto 1 do begin + c := Byte(UpCase(V[i])); + case Char(c) of + '0'..'9': c := c - 48; + 'A'..'Z': c := c - 55; + end; + if c < B then + Result := Result + Byte(c) * F; + F := F * B; + end; +end; + +function KeepIn(i, Bottom, Top: Variant): Variant; +begin + Result := i; + if Result > Top then + Result := Top + else if Result < Bottom then + Result := Bottom; +end; + +function InRange(Value, Bottom, Top: Variant): Boolean; +begin + Result := (Value >= Bottom) and (Value <= Top); +end; + +function InStrictRange(Value, Bottom, Top: Variant): Boolean; +begin + Result := (Value > Bottom) and (Value < Top); +end; + +function Min(const A, B: Integer): Integer; +begin + if A < B then + Result := A + else + Result := B; +end; + +function Min(const A: TIntA): Integer; +var + i: Integer; +begin + Result := 0; + if Length(A) = 0 then + Exit; + + Result := A[0]; + for i := 1 to High(A) do + if A[i] < Result then + Result := A[i]; +end; + +function Max(const A, B: Integer): Integer; +begin + if A > B then + Result := A + else + Result := B; +end; + +function Max(const A: TIntA): Integer; +var + i: Integer; +begin + Result := 0; + if Length(A) = 0 then + Exit; + + Result := A[0]; + for i := 1 to High(A) do + if A[i] > Result then + Result := A[i]; +end; + +function RangesOfStr(const S: string): TRanges; +var + SL: TStringList; + r, b, t: string; + i, p: Integer; + + function TryStrToCard(const S: string; out Value: Cardinal): Boolean; + var + E: Integer; + begin + Val(S, Value, E); + Result := E = 0; + end; + +begin + Result := nil; + SL := TStringList.Create; + try + Split(S, RangesSeparator, SL); + SetLength(Result, SL.Count); + for i := 0 to SL.Count-1 do begin + r := SL[i]; + with Result[i] do begin + p := CharPos(RangeInnerSeparator, r); + Simple := p = 0; // no '-' found + if Simple then begin + if r = RangeInfinite then begin // * --> *-* + Simple := False; + Bottom := Low(Bottom); + Top := High(Top); + end else if not TryStrToCard(r, Value) then + Break; + + end else begin + TileStr(r, p, p, b, t); + + if b = RangeInfinite then + Bottom := Low(Bottom) + else if not TryStrToCard(b, Bottom) then + Break; + + if t = RangeInfinite then + Top := High(Top) + else if not TryStrToCard(t, Top) then + Break; + if Bottom > Top then begin + p := Bottom; Bottom := Top; Top := p; + end; + end; + end; + end; + + if i <> SL.Count then + Result := nil; + + finally + SL.Free; + end; +end; + +function InRanges(Ranges: TRanges; TestValue: Cardinal): Boolean; +var + i: Integer; +begin + Result := True; + + for i := 0 to High(Ranges) do + with Ranges[i] do + if Simple then begin + if TestValue = Value then + Exit; + end else begin + if InRange(TestValue, Bottom, Top) then + Exit; + end; + + Result := False; +end; + +procedure WriteSL(Strings: TStrings; const Prefix: string = ''; + const Suffix: string = ''); +var + i: Integer; +begin + for i := 0 to Strings.Count-1 do + WriteLn(Prefix + Strings[i] + Suffix); +end; + +function Success(Res: Integer; ResultOnSuccess: Integer = ERROR_SUCCESS): Boolean; +begin + Result := (Res = ResultOnSuccess); + LastSuccessRes := Res; +end; + +function Failure(Res: Integer; ResultOnSuccess: Integer = ERROR_SUCCESS): Boolean; +begin + Result := not Success(Res, ResultOnSuccess); +end; + +function ExpandString(const S: string): string; +var + Len: Integer; + P, Res: PChar; +begin + Result := ''; + P := PChar(S); + Len := ExpandEnvironmentStrings(P, nil, 0); + if Len = 0 then + Exit; + + GetMem(Res, Len); + ExpandEnvironmentStrings(P, Res, Len); + + Result := Res; + FreeMem(Res, Len); +end; + +function FindAll(Strings: TStrings; const Mask: string; + ScanSubDirs: Boolean = True; Attributes: Integer = faFindEveryFile; + FileReturn: TFileNameFunc = nil): Boolean; +var + Path, FileName: string; + + procedure ScanDir(const Path, FileName: string); + var + PSR: TSearchRec; + Res: Integer; + + procedure Add(const S: string); + begin + if S <> '' then + Strings.Add(S); + end; + + begin + Res := FindFirst(Path + FileName, Attributes, PSR); + while Success(Res, 0) do begin + if Assigned(FileReturn) then + Add(FileReturn(Path + PSR.Name)) + else + Add(Path + PSR.Name); + Res := FindNext(PSR); + end; + FindClose(PSR); + if not ScanSubDirs then + Exit; + + Res := FindFirst(Path + '*', faDirectory, PSR); + while Success(Res, 0) do begin + if (PSR.Attr and faDirectory > 0) + and (PSR.Name <> '.') and (PSR.Name <> '..') then + ScanDir(Path + PSR.Name + '\', FileName); + Res := FindNext(PSR); + end; + FindClose(PSR); + end; + +begin + Strings.Clear; + Path := ExtractPath(Mask); + FileName := ExtractFileName(Mask); + ScanDir(Path, FileName); + Result := Strings.Count > 0; +end; + +function FindAllFirst(const Mask: string; ScanSubDirs: Boolean = True; + Attributes: Integer = faFindEveryFile): string; +var + Path, FileName: string; + + function ScanDir(const Path, FileName: string): Boolean; + var + PSR: TSearchRec; + Res: Integer; + begin + Result := False; + if Success(FindFirst(Path + FileName, Attributes, PSR), 0) then begin + FindAllFirst := Path + PSR.Name; + Result := True; + FindClose(PSR); + Exit; end; + if not ScanSubDirs then + Exit; + + Res := FindFirst(Path + '*', faDirectory, PSR); + while not Result and Success(Res, 0) do begin + if (PSR.Attr and faDirectory > 0) + and (PSR.Name <> '.') and (PSR.Name <> '..') then + Result := ScanDir(Path + PSR.Name + '\', FileName); + Res := FindNext(PSR); + end; + FindClose(PSR); + end; +begin + Result := ''; + Path := ExtractPath(Mask); + FileName := ExtractFileName(Mask); + ScanDir(Path, FileName); +end; + +procedure DeleteFiles(const Mask: string; ScanSubDirs: Boolean = True; + Attributes: Integer = faFindEveryFile); +var + Path, FileName: string; + + procedure ScanDir(const Path, FileName: string); + var + PSR: TSearchRec; + Res: Integer; + + procedure TryDeleteFile(const FileName: string); + begin + try + DeleteFile(Path + PSR.Name); + except + end; + end; + + begin + Res := FindFirst(Path + FileName, Attributes, PSR); + while Success(Res, 0) do begin + TryDeleteFile(Path + PSR.Name); + Res := FindNext(PSR); + end; + FindClose(PSR); + if not ScanSubDirs then + Exit; + + Res := FindFirst(Path + '*', faDirectory, PSR); + while Success(Res, 0) do begin + if (PSR.Attr and faDirectory > 0) + and (PSR.Name <> '.') and (PSR.Name <> '..') then begin + ScanDir(Path + PSR.Name + '\', FileName); + TryDeleteFile(Path + PSR.Name); + end; + Res := FindNext(PSR); + end; + FindClose(PSR); + end; +begin + Path := ExtractPath(Mask); + FileName := ExtractFileName(Mask); + ScanDir(Path, FileName); +end; + +function GetFileNew(FileName: string; NoFloppyDrives: Boolean = True): string; +var + Drive: string; + pf, pd, Len: Integer; + PSR: TSearchRec; +begin + Result := ''; + FileName := Trim(FileName); + if Length(FileName) < 2 then + Exit; + + Drive := ExtractDrive(FileName); + if not DirectoryExists(Drive) then + Exit; + + if NoFloppyDrives and (Drive[1] in ['A', 'B']) then + Exit; + + Len := Length(FileName); + Result := Drive; + pf := Length(Drive) + 1; + while pf <= Len do begin + if FileName[pf] = '\' then begin + Result := Result + '\'; + Inc(pf); + Continue; end; + + pd := CharPos('\', FileName, pf); + if pd = 0 then begin + if 0=FindFirst(Result + Copy(FileName, pf, MaxInt), faFindEveryFile, PSR) then begin + Result := Result + PSR.Name; + Break; end else begin + FindClose(PSR); + if 0=FindFirst(Result + Copy(FileName, pf, MaxInt), faDirectory, PSR) then + Result := Result + PSR.Name + '\' + else + Result := ''; + FindClose(PSR); + if Result = '' then + Break; + end; + end; + + if 0=FindFirst(Result + Copy(FileName, pf, pd - pf), faDirectory, PSR) then + Result := Result + PSR.Name + '\' + else + Result := ''; + FindClose(PSR); + if Result = '' then + Break; + + pf := pd + 1; + end; + + if (Result <> '') and not FileEx(Result, True) then + Result := ''; +end; + +function DateTimeOfFileTime(const FileTime: TFileTime): TDateTime; +var + LocalFileTime: TFileTime; + Res: Integer; +begin + Result := 0; + + FileTimeToLocalFileTime(FileTime, LocalFileTime); + if not FileTimeToDosDateTime(LocalFileTime, LongRec(Res).Hi, + LongRec(Res).Lo) then + Res := -1; + + if (Res = -1) or (Res = 0) then + Exit; + try + Result := FileDateToDateTime(Res); + except + end; +end; + +procedure FileNew(const FileName: string); +var + Handle: Integer; +begin + Handle := FileCreate(FileName); + FileClose(Handle); +end; + +function Win32PlatformStr: string; +const + PlatformStrings: array[VER_PLATFORM_WIN32s..VER_PLATFORM_WIN32_NT] of string = + ('VER_PLATFORM_WIN32s', 'VER_PLATFORM_WIN32_WINDOWS', 'VER_PLATFORM_WIN32_NT'); +begin + Result := PlatformStrings[Win32Platform]; +end; + +function FullOSInfo: string; +begin + Result := Format( + 'Platform: %s' + EOL + + 'Version: %d.%d Build %d' + EOL + + 'CSD: %s', + [ + Win32PlatformStr, + Win32MajorVersion, Win32MinorVersion, Win32BuildNumber, + Win32CSDVersion + ] + ); +end; + +function Win9x: Boolean; +begin + Result := Win32Platform = VER_PLATFORM_WIN32_WINDOWS; +end; + +function WinNT: Boolean; +begin + Result := Win32Platform = VER_PLATFORM_WIN32_NT; +end; + +function Win2000: Boolean; +begin + Result := (Win32Platform = VER_PLATFORM_WIN32_NT) + and (Win32MajorVersion = 4); +end; + +function WinXP: Boolean; +begin + Result := Win32MajorVersion >= 5; +end; + +initialization + MyDir := GetMyDir; + +end. + +unit FifoStream; + +interface + +uses Classes, windows, Dialogs; + +const + DefaultChunksize = 32768; // 32kb per chunk as default. + +type + PMemChunk = ^TMemChunk; + TMemChunk = record + Filled: Longword; + Read: Longword; + Data: pointer; + end; + + TFifo = class + private + FBuffers: TList; + FChunksize: Longword; + FCritSect: TRTLCriticalSection; + FIsWinNT: boolean; + FBytesInFifo: LongWord; + protected + function GetBytesInFifo: LongWord; + public + constructor Create; + destructor Destroy; override; + procedure Write(Data: pointer; Size: LongWord); + procedure Read(Buff: pointer; var ReqSize: LongWord); + procedure PeekData(Buff: pointer; var ReqSize: LongWord); + published + property BytesInFifo: LongWord read FBytesInFifo; + end; + +implementation + +constructor TFifo.Create; +begin + inherited; + FBuffers := TList.Create; + // set default chunksize... + FChunksize := DefaultChunksize; + InitializeCriticalSection(FCritSect); +end; + +destructor TFifo.Destroy; +var + I: Integer; +begin + EnterCriticalSection(FCritSect); + for I := 0 to FBuffers.count - 1 do + begin + FreeMem(PMemChunk(Fbuffers[I]).Data); + Dispose(PMemChunk(Fbuffers[I])); + end; + FBuffers.Clear; + FBuffers.Free; + LeaveCriticalSection(FCritSect); + + DeleteCriticalSection(FCritSect); + inherited; +end; + +function TFifo.GetBytesInFifo: LongWord; +begin + Result := 0; + if FBuffers.Count = 0 then + begin + exit; + end + else + begin + if FBuffers.Count > 1 then + Inc(Result, (FBuffers.Count - 1) * FChunkSize); + Inc(Result, PMemChunk(FBuffers[Fbuffers.Count - 1]).Filled); + Dec(Result, PMemChunk(FBuffers[0]).Read); + end; +end; + +procedure TFifo.Write(Data: pointer; Size: LongWord); +var + Privpointer: pointer; + PrivSize: LongWord; + Chunk: PMemChunk; + PosInChunk: pointer; +begin + if LongWord(Data) = 0 then + begin + // null pointer? somebody is trying to fool us, get out... + Exit; + end; + EnterCriticalSection(FCritSect); + PrivPointer := Data; + PrivSize := 0; + // are already buffers there? + if FBuffers.count > 0 then + begin + // is the last one of them not completely filled? + if PMemChunk(FBuffers[FBuffers.count - 1]).filled < FChunksize then + // not completely filled, so fill up the buffer. + begin + Chunk := PMemChunk(FBuffers[FBuffers.count - 1]); + // fetch chunkdata. + PosInChunk := Chunk.Data; + // move to current fill pos... + Inc(LongWord(PosInChunk), Chunk.Filled); + // can we fill the chunk completely? + if Size > FChunksize - Chunk.Filled then + begin + // yes we can. + Move(PrivPointer^, PosInChunk^, FChunksize - Chunk.Filled); + Inc(PrivSize, FChunksize - Chunk.Filled); + Inc(LongWord(PrivPointer), FChunksize - Chunk.Filled); + Chunk.Filled := FChunkSize; + end + else + // we have to less data for filling the chunk completely, + // just put everything in. + begin + Move(PrivPointer^, PosInChunk^, Size); + Inc(PrivSize, Size); + Inc(Chunk.Filled, Size); + end; + end; + end; + // as long as we have remaining stuff put it into new chunks. + while (PrivSize < Size) do + begin + new(Chunk); + GetMem(Chunk.Data, FChunksize); + Chunk.Read := 0; + // can we fill an entire chunk with the remaining data? + if Privsize + FChunksize < Size then + begin + // yes we can, so put the stuff in. + Move(Privpointer^, Chunk.Data^, FChunksize); + Inc(LongWord(PrivPointer), FChunksize); + Inc(PrivSize, FChunksize); + Chunk.Filled := FChunksize; + end + else // we have to less data to fill the entire chunk, just put the remaining stuff in. + begin + Move(Privpointer^, Chunk.Data^, Size - Privsize); + Chunk.Filled := Size - Privsize; + Inc(PrivSize, Size - Privsize); + end; + Fbuffers.Add(Chunk); + end; + if Size <> Privsize then + Showmessage('miscalculation in TFifo.write'); + FBytesInFifo := GetBytesInFifo; + LeaveCriticalSection(FCritSect); +end; + +procedure TFifo.Read(Buff: pointer; var ReqSize: LongWord); +var + PrivSize: Integer; + Privpos: pointer; + Chunk: PMemChunk; + ChunkPos: pointer; +begin + if LongWord(Buff) = 0 then + begin + // null pointer? somebody is trying to fool us, get out... + Exit; + end; + EnterCriticalSection(FCritSect); + PrivSize := 0; + Privpos := Buff; + while FBuffers.Count > 0 do + begin + Chunk := PMemChunk(FBuffers[0]); + ChunkPos := Chunk.data; + Inc(LongWord(ChunkPos), Chunk.Read); + // does the remaining part of the chunk fit into the buffer? + if PrivSize + (Chunk.Filled - Chunk.read) < ReqSize then + begin // yep, it fits + Move(ChunkPos^, Privpos^, Chunk.Filled - Chunk.read); + Inc(PrivSize, Chunk.Filled - Chunk.read); + FreeMem(Chunk.Data); + Dispose(Chunk); + FBuffers.Delete(0); + end + else // remaining part didn't fit, get as much as we can and increment the + // read attribute. + begin + Move(ChunkPos^, Privpos^, ReqSize - PrivSize); + Inc(Chunk.read, ReqSize - PrivSize); + Inc(PrivSize, ReqSize - PrivSize); + // as we filled the buffer, we'll have to break here. + break; + end; + end; + FBytesInFifo := GetBytesInFifo; + LeaveCriticalSection(FCritSect); + ReqSize := PrivSize; +end; + +// read Data from Stream without removing it from the Stream... + +procedure TFifo.PeekData(Buff: pointer; var ReqSize: LongWord); +var + PrivSize: Integer; + Privpos: pointer; + Chunk: PMemChunk; + ChunkPos: pointer; + ChunkNr: Integer; +begin + if LongWord(Buff) = 0 then + begin + // null pointer? somebody is trying to fool us, get out... + Exit; + end; + EnterCriticalSection(FCritSect); + PrivSize := 0; + Privpos := Buff; + ChunkNr := 0; + while FBuffers.Count > ChunkNr do + begin + Chunk := PMemChunk(FBuffers[ChunkNr]); + ChunkPos := Chunk.data; + Inc(LongWord(ChunkPos), Chunk.Read); + // does the remaining part of the chunk fit into the buffer? + if PrivSize + (Chunk.Filled - Chunk.read) < ReqSize then + begin // yep, it fits + Move(ChunkPos^, Privpos^, Chunk.Filled - Chunk.read); + Inc(PrivSize, Chunk.Filled - Chunk.read); + Inc(ChunkNr); + end + else // remaining part didn't fit, get as much as we can and increment the + // read attribute. + begin + Move(ChunkPos^, Privpos^, ReqSize - PrivSize); + Inc(PrivSize, ReqSize - PrivSize); + // as we filled the buffer, we'll have to break here. + break; + end; + end; + LeaveCriticalSection(FCritSect); + ReqSize := PrivSize; +end; + +end. diff --git a/tests/examplefiles/example.rb b/tests/examplefiles/example.rb new file mode 100644 index 0000000..93f8dc2 --- /dev/null +++ b/tests/examplefiles/example.rb @@ -0,0 +1,1852 @@ +module CodeRay + module Scanners + +class Ruby < Scanner + + RESERVED_WORDS = [ + 'and', 'def', 'end', 'in', 'or', 'unless', 'begin', + 'defined?', 'ensure', 'module', 'redo', 'super', 'until', + 'BEGIN', 'break', 'do', 'next', 'rescue', 'then', + 'when', 'END', 'case', 'else', 'for', 'retry', + 'while', 'alias', 'class', 'elsif', 'if', 'not', 'return', + 'undef', 'yield', + ] + + DEF_KEYWORDS = ['def'] + MODULE_KEYWORDS = ['class', 'module'] + DEF_NEW_STATE = WordList.new(:initial). + add(DEF_KEYWORDS, :def_expected). + add(MODULE_KEYWORDS, :module_expected) + + WORDS_ALLOWING_REGEXP = [ + 'and', 'or', 'not', 'while', 'until', 'unless', 'if', 'elsif', 'when' + ] + REGEXP_ALLOWED = WordList.new(false). + add(WORDS_ALLOWING_REGEXP, :set) + + PREDEFINED_CONSTANTS = [ + 'nil', 'true', 'false', 'self', + 'DATA', 'ARGV', 'ARGF', '__FILE__', '__LINE__', + ] + + IDENT_KIND = WordList.new(:ident). + add(RESERVED_WORDS, :reserved). + add(PREDEFINED_CONSTANTS, :pre_constant) + + METHOD_NAME = / #{IDENT} [?!]? /xo + METHOD_NAME_EX = / + #{METHOD_NAME} # common methods: split, foo=, empty?, gsub! + | \*\*? # multiplication and power + | [-+~]@? # plus, minus + | [\/%&|^`] # division, modulo or format strings, &and, |or, ^xor, `system` + | \[\]=? # array getter and setter + | <=?>? | >=? # comparison, rocket operator + | << | >> # append or shift left, shift right + | ===? # simple equality and case equality + /ox + GLOBAL_VARIABLE = / \$ (?: #{IDENT} | \d+ | [~&+`'=\/,;_.<>!@0$?*":F\\] | -[a-zA-Z_0-9] ) /ox + + DOUBLEQ = / " [^"\#\\]* (?: (?: \#\{.*?\} | \#(?:$")? | \\. ) [^"\#\\]* )* "? /ox + SINGLEQ = / ' [^'\\]* (?: \\. [^'\\]* )* '? /ox + STRING = / #{SINGLEQ} | #{DOUBLEQ} /ox + SHELL = / ` [^`\#\\]* (?: (?: \#\{.*?\} | \#(?:$`)? | \\. ) [^`\#\\]* )* `? /ox + REGEXP = / \/ [^\/\#\\]* (?: (?: \#\{.*?\} | \#(?:$\/)? | \\. ) [^\/\#\\]* )* \/? /ox + + DECIMAL = /\d+(?:_\d+)*/ # doesn't recognize 09 as octal error + OCTAL = /0_?[0-7]+(?:_[0-7]+)*/ + HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/ + BINARY = /0b[01]+(?:_[01]+)*/ + + EXPONENT = / [eE] [+-]? #{DECIMAL} /ox + FLOAT = / #{DECIMAL} (?: #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? ) / + INTEGER = /#{OCTAL}|#{HEXADECIMAL}|#{BINARY}|#{DECIMAL}/ + + def reset + super + @regexp_allowed = false + end + + def next_token + return if @scanner.eos? + + kind = :error + if @scanner.scan(/\s+/) # in every state + kind = :space + @regexp_allowed = :set if @regexp_allowed or @scanner.matched.index(?\n) # delayed flag setting + + elsif @state == :def_expected + if @scanner.scan(/ (?: (?:#{IDENT}(?:\.|::))* | (?:@@?|$)? #{IDENT}(?:\.|::) ) #{METHOD_NAME_EX} /ox) + kind = :method + @state = :initial + else + @scanner.getch + end + @state = :initial + + elsif @state == :module_expected + if @scanner.scan(/<#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\\\\])(?:(?!\1)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\1)[^#\\\\])*)*\1?)|\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\s\\\\])(?:(?!\2)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\2)[^#\\\\])*)*\2?|\\\\[^#\\\\]*(?:(?:#\{.*?\}|#)[^#\\\\]*)*\\\\?)/ + elsif @scanner.scan(/:(?:#{GLOBAL_VARIABLE}|#{METHOD_NAME_EX}|#{STRING})/ox) + kind = :symbol + elsif @scanner.scan(/ + \? (?: + [^\s\\] + | + \\ (?:M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M-))? (?: \\ (?: . | [0-7]{3} | x[0-9A-Fa-f][0-9A-Fa-f] ) + ) + /mox) + kind = :integer + + elsif @scanner.scan(/ [-+*\/%=<>;,|&!()\[\]{}~?] | \.\.?\.? | ::? /x) + kind = :operator + @regexp_allowed = :set if @scanner.matched[-1,1] =~ /[~=!<>|&^,\(\[+\-\/\*%]\z/ + elsif @scanner.scan(FLOAT) + kind = :float + elsif @scanner.scan(INTEGER) + kind = :integer + else + @scanner.getch + end + end + + token = Token.new @scanner.matched, kind + + if kind == :regexp + token.text << @scanner.scan(/[eimnosux]*/) + end + + @regexp_allowed = (@regexp_allowed == :set) # delayed flag setting + + token + end +end + +register Ruby, 'ruby', 'rb' + + end +end +class Set + include Enumerable + + # Creates a new set containing the given objects. + def self.[](*ary) + new(ary) + end + + # Creates a new set containing the elements of the given enumerable + # object. + # + # If a block is given, the elements of enum are preprocessed by the + # given block. + def initialize(enum = nil, &block) # :yields: o + @hash ||= Hash.new + + enum.nil? and return + + if block + enum.each { |o| add(block[o]) } + else + merge(enum) + end + end + + # Copy internal hash. + def initialize_copy(orig) + @hash = orig.instance_eval{@hash}.dup + end + + # Returns the number of elements. + def size + @hash.size + end + alias length size + + # Returns true if the set contains no elements. + def empty? + @hash.empty? + end + + # Removes all elements and returns self. + def clear + @hash.clear + self + end + + # Replaces the contents of the set with the contents of the given + # enumerable object and returns self. + def replace(enum) + if enum.class == self.class + @hash.replace(enum.instance_eval { @hash }) + else + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + clear + enum.each { |o| add(o) } + end + + self + end + + # Converts the set to an array. The order of elements is uncertain. + def to_a + @hash.keys + end + + def flatten_merge(set, seen = Set.new) + set.each { |e| + if e.is_a?(Set) + if seen.include?(e_id = e.object_id) + raise ArgumentError, "tried to flatten recursive Set" + end + + seen.add(e_id) + flatten_merge(e, seen) + seen.delete(e_id) + else + add(e) + end + } + + self + end + protected :flatten_merge + + # Returns a new set that is a copy of the set, flattening each + # containing set recursively. + def flatten + self.class.new.flatten_merge(self) + end + + # Equivalent to Set#flatten, but replaces the receiver with the + # result in place. Returns nil if no modifications were made. + def flatten! + if detect { |e| e.is_a?(Set) } + replace(flatten()) + else + nil + end + end + + # Returns true if the set contains the given object. + def include?(o) + @hash.include?(o) + end + alias member? include? + + # Returns true if the set is a superset of the given set. + def superset?(set) + set.is_a?(Set) or raise ArgumentError, "value must be a set" + return false if size < set.size + set.all? { |o| include?(o) } + end + + # Returns true if the set is a proper superset of the given set. + def proper_superset?(set) + set.is_a?(Set) or raise ArgumentError, "value must be a set" + return false if size <= set.size + set.all? { |o| include?(o) } + end + + # Returns true if the set is a subset of the given set. + def subset?(set) + set.is_a?(Set) or raise ArgumentError, "value must be a set" + return false if set.size < size + all? { |o| set.include?(o) } + end + + # Returns true if the set is a proper subset of the given set. + def proper_subset?(set) + set.is_a?(Set) or raise ArgumentError, "value must be a set" + return false if set.size <= size + all? { |o| set.include?(o) } + end + + # Calls the given block once for each element in the set, passing + # the element as parameter. + def each + @hash.each_key { |o| yield(o) } + self + end + + # Adds the given object to the set and returns self. Use +merge+ to + # add several elements at once. + def add(o) + @hash[o] = true + self + end + alias << add + + # Adds the given object to the set and returns self. If the + # object is already in the set, returns nil. + def add?(o) + if include?(o) + nil + else + add(o) + end + end + + # Deletes the given object from the set and returns self. Use +subtract+ to + # delete several items at once. + def delete(o) + @hash.delete(o) + self + end + + # Deletes the given object from the set and returns self. If the + # object is not in the set, returns nil. + def delete?(o) + if include?(o) + delete(o) + else + nil + end + end + + # Deletes every element of the set for which block evaluates to + # true, and returns self. + def delete_if + @hash.delete_if { |o,| yield(o) } + self + end + + # Do collect() destructively. + def collect! + set = self.class.new + each { |o| set << yield(o) } + replace(set) + end + alias map! collect! + + # Equivalent to Set#delete_if, but returns nil if no changes were + # made. + def reject! + n = size + delete_if { |o| yield(o) } + size == n ? nil : self + end + + # Merges the elements of the given enumerable object to the set and + # returns self. + def merge(enum) + if enum.is_a?(Set) + @hash.update(enum.instance_eval { @hash }) + else + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + enum.each { |o| add(o) } + end + + self + end + + # Deletes every element that appears in the given enumerable object + # and returns self. + def subtract(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + enum.each { |o| delete(o) } + self + end + + # Returns a new set built by merging the set and the elements of the + # given enumerable object. + def |(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + dup.merge(enum) + end + alias + | ## + alias union | ## + + # Returns a new set built by duplicating the set, removing every + # element that appears in the given enumerable object. + def -(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + dup.subtract(enum) + end + alias difference - ## + + # Returns a new array containing elements common to the set and the + # given enumerable object. + def &(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + n = self.class.new + enum.each { |o| n.add(o) if include?(o) } + n + end + alias intersection & ## + + # Returns a new array containing elements exclusive between the set + # and the given enumerable object. (set ^ enum) is equivalent to + # ((set | enum) - (set & enum)). + def ^(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + n = dup + enum.each { |o| if n.include?(o) then n.delete(o) else n.add(o) end } + n + end + + # Returns true if two sets are equal. The equality of each couple + # of elements is defined according to Object#eql?. + def ==(set) + equal?(set) and return true + + set.is_a?(Set) && size == set.size or return false + + hash = @hash.dup + set.all? { |o| hash.include?(o) } + end + + def hash # :nodoc: + @hash.hash + end + + def eql?(o) # :nodoc: + return false unless o.is_a?(Set) + @hash.eql?(o.instance_eval{@hash}) + end + + # Classifies the set by the return value of the given block and + # returns a hash of {value => set of elements} pairs. The block is + # called once for each element of the set, passing the element as + # parameter. + # + # e.g.: + # + # require 'set' + # files = Set.new(Dir.glob("*.rb")) + # hash = files.classify { |f| File.mtime(f).year } + # p hash # => {2000=>#, + # # 2001=>#, + # # 2002=>#} + def classify # :yields: o + h = {} + + each { |i| + x = yield(i) + (h[x] ||= self.class.new).add(i) + } + + h + end + + # Divides the set into a set of subsets according to the commonality + # defined by the given block. + # + # If the arity of the block is 2, elements o1 and o2 are in common + # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are + # in common if block.call(o1) == block.call(o2). + # + # e.g.: + # + # require 'set' + # numbers = Set[1, 3, 4, 6, 9, 10, 11] + # set = numbers.divide { |i,j| (i - j).abs == 1 } + # p set # => #, + # # #, + # # #, + # # #}> + def divide(&func) + if func.arity == 2 + require 'tsort' + + class << dig = {} # :nodoc: + include TSort + + alias tsort_each_node each_key + def tsort_each_child(node, &block) + fetch(node).each(&block) + end + end + + each { |u| + dig[u] = a = [] + each{ |v| func.call(u, v) and a << v } + } + + set = Set.new() + dig.each_strongly_connected_component { |css| + set.add(self.class.new(css)) + } + set + else + Set.new(classify(&func).values) + end + end + + InspectKey = :__inspect_key__ # :nodoc: + + # Returns a string containing a human-readable representation of the + # set. ("#") + def inspect + ids = (Thread.current[InspectKey] ||= []) + + if ids.include?(object_id) + return sprintf('#<%s: {...}>', self.class.name) + end + + begin + ids << object_id + return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) + ensure + ids.pop + end + end + + def pretty_print(pp) # :nodoc: + pp.text sprintf('#<%s: {', self.class.name) + pp.nest(1) { + pp.seplist(self) { |o| + pp.pp o + } + } + pp.text "}>" + end + + def pretty_print_cycle(pp) # :nodoc: + pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...') + end +end + +# SortedSet implements a set which elements are sorted in order. See Set. +class SortedSet < Set + @@setup = false + + class << self + def [](*ary) # :nodoc: + new(ary) + end + + def setup # :nodoc: + @@setup and return + + begin + require 'rbtree' + + module_eval %{ + def initialize(*args, &block) + @hash = RBTree.new + super + end + } + rescue LoadError + module_eval %{ + def initialize(*args, &block) + @keys = nil + super + end + + def clear + @keys = nil + super + end + + def replace(enum) + @keys = nil + super + end + + def add(o) + @keys = nil + @hash[o] = true + self + end + alias << add + + def delete(o) + @keys = nil + @hash.delete(o) + self + end + + def delete_if + n = @hash.size + @hash.delete_if { |o,| yield(o) } + @keys = nil if @hash.size != n + self + end + + def merge(enum) + @keys = nil + super + end + + def each + to_a.each { |o| yield(o) } + end + + def to_a + (@keys = @hash.keys).sort! unless @keys + @keys + end + } + end + + @@setup = true + end + end + + def initialize(*args, &block) # :nodoc: + SortedSet.setup + initialize(*args, &block) + end +end + +module Enumerable + # Makes a set from the enumerable object with given arguments. + def to_set(klass = Set, *args, &block) + klass.new(self, *args, &block) + end +end + +# =begin +# == RestricedSet class +# RestricedSet implements a set with restrictions defined by a given +# block. +# +# === Super class +# Set +# +# === Class Methods +# --- RestricedSet::new(enum = nil) { |o| ... } +# --- RestricedSet::new(enum = nil) { |rset, o| ... } +# Creates a new restricted set containing the elements of the given +# enumerable object. Restrictions are defined by the given block. +# +# If the block's arity is 2, it is called with the RestrictedSet +# itself and an object to see if the object is allowed to be put in +# the set. +# +# Otherwise, the block is called with an object to see if the object +# is allowed to be put in the set. +# +# === Instance Methods +# --- restriction_proc +# Returns the restriction procedure of the set. +# +# =end +# +# class RestricedSet < Set +# def initialize(*args, &block) +# @proc = block or raise ArgumentError, "missing a block" +# +# if @proc.arity == 2 +# instance_eval %{ +# def add(o) +# @hash[o] = true if @proc.call(self, o) +# self +# end +# alias << add +# +# def add?(o) +# if include?(o) || !@proc.call(self, o) +# nil +# else +# @hash[o] = true +# self +# end +# end +# +# def replace(enum) +# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" +# clear +# enum.each { |o| add(o) } +# +# self +# end +# +# def merge(enum) +# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" +# enum.each { |o| add(o) } +# +# self +# end +# } +# else +# instance_eval %{ +# def add(o) +# if @proc.call(o) +# @hash[o] = true +# end +# self +# end +# alias << add +# +# def add?(o) +# if include?(o) || !@proc.call(o) +# nil +# else +# @hash[o] = true +# self +# end +# end +# } +# end +# +# super(*args) +# end +# +# def restriction_proc +# @proc +# end +# end + +if $0 == __FILE__ + eval DATA.read, nil, $0, __LINE__+4 +end + +# = rweb - CGI Support Library +# +# Author:: Johannes Barre (mailto:rweb@igels.net) +# Copyright:: Copyright (c) 2003, 04 by Johannes Barre +# License:: GNU Lesser General Public License (COPYING, http://www.gnu.org/copyleft/lesser.html) +# Version:: 0.1.0 +# CVS-ID:: $Id: example.rb 39 2005-11-05 03:33:55Z murphy $ +# +# == What is Rweb? +# Rweb is a replacement for the cgi class included in the ruby distribution. +# +# == How to use +# +# === Basics +# +# This class is made to be as easy as possible to use. An example: +# +# require "rweb" +# +# web = Rweb.new +# web.out do +# web.puts "Hello world!" +# end +# +# The visitor will get a simple "Hello World!" in his browser. Please notice, +# that won't set html-tags for you, so you should better do something like this: +# +# require "rweb" +# +# web = Rweb.new +# web.out do +# web.puts "Hello world!" +# end +# +# === Set headers +# Of course, it's also possible to tell the browser, that the content of this +# page is plain text instead of html code: +# +# require "rweb" +# +# web = Rweb.new +# web.out do +# web.header("content-type: text/plain") +# web.puts "Hello plain world!" +# end +# +# Please remember, headers can't be set after the page content has been send. +# You have to set all nessessary headers before the first puts oder print. It's +# possible to cache the content until everything is complete. Doing it this +# way, you can set headers everywhere. +# +# If you set a header twice, the second header will replace the first one. The +# header name is not casesensitive, it will allways converted in to the +# capitalised form suggested by the w3c (http://w3.org) +# +# === Set cookies +# Setting cookies is quite easy: +# include 'rweb' +# +# web = Rweb.new +# Cookie.new("Visits", web.cookies['visits'].to_i +1) +# web.out do +# web.puts "Welcome back! You visited this page #{web.cookies['visits'].to_i +1} times" +# end +# +# See the class Cookie for more details. +# +# === Get form and cookie values +# There are four ways to submit data from the browser to the server and your +# ruby script: via GET, POST, cookies and file upload. Rweb doesn't support +# file upload by now. +# +# include 'rweb' +# +# web = Rweb.new +# web.out do +# web.print "action: #{web.get['action']} " +# web.puts "The value of the cookie 'visits' is #{web.cookies['visits']}" +# web.puts "The post parameter 'test['x']' is #{web.post['test']['x']}" +# end + +RWEB_VERSION = "0.1.0" +RWEB = "rweb/#{RWEB_VERSION}" + +#require 'rwebcookie' -> edit by bunny :-) + +class Rweb + # All parameter submitted via the GET method are available in attribute + # get. This is Hash, where every parameter is available as a key-value + # pair. + # + # If your input tag has a name like this one, it's value will be available + # as web.get["fieldname"] + # + # You can submit values as a Hash + # + # + # will be available as + # web.get["text"]["index"] + # web.get["text"]["index2"] + # Integers are also possible + # + # + # + # will be available as + # web.get["int"][0] # First Field + # web.get["int"][1] # Second one + # Please notice, this doesn'd work like you might expect: + # + # It will not be available as web.get["text"]["index"] but + # web.get["text[index]"] + attr_reader :get + + # All parameters submitted via POST are available in the attribute post. It + # works like the get attribute. + # + # will be available as + # web.post["text"][0] + attr_reader :post + + # All cookies submitted by the browser are available in cookies. This is a + # Hash, where every cookie is a key-value pair. + attr_reader :cookies + + # The name of the browser identification is submitted as USER_AGENT and + # available in this attribute. + attr_reader :user_agent + + # The IP address of the client. + attr_reader :remote_addr + + # Creates a new Rweb object. This should only done once. You can set various + # options via the settings hash. + # + # "cache" => true: Everything you script send to the client will be cached + # until the end of the out block or until flush is called. This way, you + # can modify headers and cookies even after printing something to the client. + # + # "safe" => level: Changes the $SAFE attribute. By default, $SAFE will be set + # to 1. If $SAFE is already higher than this value, it won't be changed. + # + # "silend" => true: Normaly, Rweb adds automaticly a header like this + # "X-Powered-By: Rweb/x.x.x (Ruby/y.y.y)". With the silend option you can + # suppress this. + def initialize (settings = {}) + # {{{ + @header = {} + @cookies = {} + @get = {} + @post = {} + + # Internal attributes + @status = nil + @reasonPhrase = nil + @setcookies = [] + @output_started = false; + @output_allowed = false; + + @mod_ruby = false + @env = ENV.to_hash + + if defined?(MOD_RUBY) + @output_method = "mod_ruby" + @mod_ruby = true + elsif @env['SERVER_SOFTWARE'] =~ /^Microsoft-IIS/i + @output_method = "nph" + else + @output_method = "ph" + end + + unless settings.is_a?(Hash) + raise TypeError, "settings must be a Hash" + end + @settings = settings + + unless @settings.has_key?("safe") + @settings["safe"] = 1 + end + + if $SAFE < @settings["safe"] + $SAFE = @settings["safe"] + end + + unless @settings.has_key?("cache") + @settings["cache"] = false + end + + # mod_ruby sets no QUERY_STRING variable, if no GET-Parameters are given + unless @env.has_key?("QUERY_STRING") + @env["QUERY_STRING"] = "" + end + + # Now we split the QUERY_STRING by the seperators & and ; or, if + # specified, settings['get seperator'] + unless @settings.has_key?("get seperator") + get_args = @env['QUERY_STRING'].split(/[&;]/) + else + get_args = @env['QUERY_STRING'].split(@settings['get seperator']) + end + + get_args.each do | arg | + arg_key, arg_val = arg.split(/=/, 2) + arg_key = Rweb::unescape(arg_key) + arg_val = Rweb::unescape(arg_val) + + # Parse names like name[0], name['text'] or name[] + pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/ + keys = [] + while match = pattern.match(arg_key) + arg_key = match[1] + keys = [match[2]] + keys + end + keys = [arg_key] + keys + + akt = @get + last = nil + lastkey = nil + keys.each do |key| + if key == "" + # No key specified (like in "test[]"), so we use the + # lowerst unused Integer as key + key = 0 + while akt.has_key?(key) + key += 1 + end + elsif /^[0-9]*$/ =~ key + # If the index is numerical convert it to an Integer + key = key.to_i + elsif key[0].chr == "'" || key[0].chr == '"' + key = key[1, key.length() -2] + end + if !akt.has_key?(key) || !akt[key].class == Hash + # create an empty Hash if there isn't already one + akt[key] = {} + end + last = akt + lastkey = key + akt = akt[key] + end + last[lastkey] = arg_val + end + + if @env['REQUEST_METHOD'] == "POST" + if @env.has_key?("CONTENT_TYPE") && @env['CONTENT_TYPE'] == "application/x-www-form-urlencoded" && @env.has_key?('CONTENT_LENGTH') + unless @settings.has_key?("post seperator") + post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(/[&;]/) + else + post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(@settings['post seperator']) + end + post_args.each do | arg | + arg_key, arg_val = arg.split(/=/, 2) + arg_key = Rweb::unescape(arg_key) + arg_val = Rweb::unescape(arg_val) + + # Parse names like name[0], name['text'] or name[] + pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/ + keys = [] + while match = pattern.match(arg_key) + arg_key = match[1] + keys = [match[2]] + keys + end + keys = [arg_key] + keys + + akt = @post + last = nil + lastkey = nil + keys.each do |key| + if key == "" + # No key specified (like in "test[]"), so we use + # the lowerst unused Integer as key + key = 0 + while akt.has_key?(key) + key += 1 + end + elsif /^[0-9]*$/ =~ key + # If the index is numerical convert it to an Integer + key = key.to_i + elsif key[0].chr == "'" || key[0].chr == '"' + key = key[1, key.length() -2] + end + if !akt.has_key?(key) || !akt[key].class == Hash + # create an empty Hash if there isn't already one + akt[key] = {} + end + last = akt + lastkey = key + akt = akt[key] + end + last[lastkey] = arg_val + end + else + # Maybe we should print a warning here? + $stderr.print("Unidentified form data recived and discarded.") + end + end + + if @env.has_key?("HTTP_COOKIE") + cookie = @env['HTTP_COOKIE'].split(/; ?/) + cookie.each do | c | + cookie_key, cookie_val = c.split(/=/, 2) + + @cookies [Rweb::unescape(cookie_key)] = Rweb::unescape(cookie_val) + end + end + + if defined?(@env['HTTP_USER_AGENT']) + @user_agent = @env['HTTP_USER_AGENT'] + else + @user_agent = nil; + end + + if defined?(@env['REMOTE_ADDR']) + @remote_addr = @env['REMOTE_ADDR'] + else + @remote_addr = nil + end + # }}} + end + + # Prints a String to the client. If caching is enabled, the String will + # buffered until the end of the out block ends. + def print(str = "") + # {{{ + unless @output_allowed + raise "You just can write to output inside of a Rweb::out-block" + end + + if @settings["cache"] + @buffer += [str.to_s] + else + unless @output_started + sendHeaders + end + $stdout.print(str) + end + nil + # }}} + end + + # Prints a String to the client and adds a line break at the end. Please + # remember, that a line break is not visible in HTML, use the
HTML-Tag + # for this. If caching is enabled, the String will buffered until the end + # of the out block ends. + def puts(str = "") + # {{{ + self.print(str + "\n") + # }}} + end + + # Alias to print. + def write(str = "") + # {{{ + self.print(str) + # }}} + end + + # If caching is enabled, all cached data are send to the cliend and the + # cache emptied. + def flush + # {{{ + unless @output_allowed + raise "You can't use flush outside of a Rweb::out-block" + end + buffer = @buffer.join + + unless @output_started + sendHeaders + end + $stdout.print(buffer) + + @buffer = [] + # }}} + end + + # Sends one or more header to the client. All headers are cached just + # before body data are send to the client. If the same header are set + # twice, only the last value is send. + # + # Example: + # web.header("Last-Modified: Mon, 16 Feb 2004 20:15:41 GMT") + # web.header("Location: http://www.ruby-lang.org") + # + # You can specify more than one header at the time by doing something like + # this: + # web.header("Content-Type: text/plain\nContent-Length: 383") + # or + # web.header(["Content-Type: text/plain", "Content-Length: 383"]) + def header(str) + # {{{ + if @output_started + raise "HTTP-Headers are already send. You can't change them after output has started!" + end + unless @output_allowed + raise "You just can set headers inside of a Rweb::out-block" + end + if str.is_a?Array + str.each do | value | + self.header(value) + end + + elsif str.split(/\n/).length > 1 + str.split(/\n/).each do | value | + self.header(value) + end + + elsif str.is_a? String + str.gsub!(/\r/, "") + + if (str =~ /^HTTP\/1\.[01] [0-9]{3} ?.*$/) == 0 + pattern = /^HTTP\/1.[01] ([0-9]{3}) ?(.*)$/ + + result = pattern.match(str) + self.setstatus(result[0], result[1]) + elsif (str =~ /^status: [0-9]{3} ?.*$/i) == 0 + pattern = /^status: ([0-9]{3}) ?(.*)$/i + + result = pattern.match(str) + self.setstatus(result[0], result[1]) + else + a = str.split(/: ?/, 2) + + @header[a[0].downcase] = a[1] + end + end + # }}} + end + + # Changes the status of this page. There are several codes like "200 OK", + # "302 Found", "404 Not Found" or "500 Internal Server Error". A list of + # all codes is available at + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 + # + # You can just send the code number, the reason phrase will be added + # automaticly with the recommendations from the w3c if not specified. If + # you set the status twice or more, only the last status will be send. + # Examples: + # web.status("401 Unauthorized") + # web.status("410 Sad but true, this lonely page is gone :(") + # web.status(206) + # web.status("400") + # + # The default status is "200 OK". If a "Location" header is set, the + # default status is "302 Found". + def status(str) + # {{{ + if @output_started + raise "HTTP-Headers are already send. You can't change them after output has started!" + end + unless @output_allowed + raise "You just can set headers inside of a Rweb::out-block" + end + if str.is_a?Integer + @status = str + elsif str.is_a?String + p1 = /^([0-9]{3}) ?(.*)$/ + p2 = /^HTTP\/1\.[01] ([0-9]{3}) ?(.*)$/ + p3 = /^status: ([0-9]{3}) ?(.*)$/i + + if (a = p1.match(str)) == nil + if (a = p2.match(str)) == nil + if (a = p3.match(str)) == nil + raise ArgumentError, "Invalid argument", caller + end + end + end + @status = a[1].to_i + if a[2] != "" + @reasonPhrase = a[2] + else + @reasonPhrase = getReasonPhrase(@status) + end + else + raise ArgumentError, "Argument of setstatus must be integer or string", caller + end + # }}} + end + + # Handles the output of your content and rescues all exceptions. Send all + # data in the block to this method. For example: + # web.out do + # web.header("Content-Type: text/plain") + # web.puts("Hello, plain world!") + # end + def out + # {{{ + @output_allowed = true + @buffer = []; # We use an array as buffer, because it's more performant :) + + begin + yield + rescue Exception => exception + $stderr.puts "Ruby exception rescued (#{exception.class}): #{exception.message}" + $stderr.puts exception.backtrace.join("\n") + + unless @output_started + self.setstatus(500) + @header = {} + end + + unless (@settings.has_key?("hide errors") and @settings["hide errors"] == true) + unless @output_started + self.header("Content-Type: text/html") + self.puts "" + self.puts "" + self.puts "" + self.puts "500 Internal Server Error" + self.puts "" + self.puts "" + end + if @header.has_key?("content-type") and (@header["content-type"] =~ /^text\/html/i) == 0 + self.puts "

Internal Server Error

" + self.puts "

The server encountered an exception and was unable to complete your request.

" + self.puts "

The exception has provided the following information:

" + self.puts "
#{exception.class}: #{exception.message} on"
+                    self.puts
+                    self.puts "#{exception.backtrace.join("\n")}
" + self.puts "" + self.puts "" + else + self.puts "The server encountered an exception and was unable to complete your request" + self.puts "The exception has provided the following information:" + self.puts "#{exception.class}: #{exception.message}" + self.puts + self.puts exception.backtrace.join("\n") + end + end + end + + if @settings["cache"] + buffer = @buffer.join + + unless @output_started + unless @header.has_key?("content-length") + self.header("content-length: #{buffer.length}") + end + + sendHeaders + end + $stdout.print(buffer) + elsif !@output_started + sendHeaders + end + @output_allowed = false; + # }}} + end + + # Decodes URL encoded data, %20 for example stands for a space. + def Rweb.unescape(str) + # {{{ + if defined? str and str.is_a? String + str.gsub!(/\+/, " ") + str.gsub(/%.{2}/) do | s | + s[1,2].hex.chr + end + end + # }}} + end + + protected + def sendHeaders + # {{{ + + Cookie.disallow # no more cookies can be set or modified + if !(@settings.has_key?("silent") and @settings["silent"] == true) and !@header.has_key?("x-powered-by") + if @mod_ruby + header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION}, #{MOD_RUBY})"); + else + header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION})"); + end + end + + if @output_method == "ph" + if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location")) + header("content-type: text/html") + end + + if @status != nil + $stdout.print "Status: #{@status} #{@reasonPhrase}\r\n" + end + + @header.each do |key, value| + key = key *1 # "unfreeze" key :) + key[0] = key[0,1].upcase![0] + + key = key.gsub(/-[a-z]/) do |char| + "-" + char[1,1].upcase + end + + $stdout.print "#{key}: #{value}\r\n" + end + cookies = Cookie.getHttpHeader # Get all cookies as an HTTP Header + if cookies + $stdout.print cookies + end + + $stdout.print "\r\n" + + elsif @output_method == "nph" + elsif @output_method == "mod_ruby" + r = Apache.request + + if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location")) + header("text/html") + end + + if @status != nil + r.status_line = "#{@status} #{@reasonPhrase}" + end + + r.send_http_header + @header.each do |key, value| + key = key *1 # "unfreeze" key :) + + key[0] = key[0,1].upcase![0] + key = key.gsub(/-[a-z]/) do |char| + "-" + char[1,1].upcase + end + puts "#{key}: #{value.class}" + #r.headers_out[key] = value + end + end + @output_started = true + # }}} + end + + def getReasonPhrase (status) + # {{{ + if status == 100 + "Continue" + elsif status == 101 + "Switching Protocols" + elsif status == 200 + "OK" + elsif status == 201 + "Created" + elsif status == 202 + "Accepted" + elsif status == 203 + "Non-Authoritative Information" + elsif status == 204 + "No Content" + elsif status == 205 + "Reset Content" + elsif status == 206 + "Partial Content" + elsif status == 300 + "Multiple Choices" + elsif status == 301 + "Moved Permanently" + elsif status == 302 + "Found" + elsif status == 303 + "See Other" + elsif status == 304 + "Not Modified" + elsif status == 305 + "Use Proxy" + elsif status == 307 + "Temporary Redirect" + elsif status == 400 + "Bad Request" + elsif status == 401 + "Unauthorized" + elsif status == 402 + "Payment Required" + elsif status == 403 + "Forbidden" + elsif status == 404 + "Not Found" + elsif status == 405 + "Method Not Allowed" + elsif status == 406 + "Not Acceptable" + elsif status == 407 + "Proxy Authentication Required" + elsif status == 408 + "Request Time-out" + elsif status == 409 + "Conflict" + elsif status == 410 + "Gone" + elsif status == 411 + "Length Required" + elsif status == 412 + "Precondition Failed" + elsif status == 413 + "Request Entity Too Large" + elsif status == 414 + "Request-URI Too Large" + elsif status == 415 + "Unsupported Media Type" + elsif status == 416 + "Requested range not satisfiable" + elsif status == 417 + "Expectation Failed" + elsif status == 500 + "Internal Server Error" + elsif status == 501 + "Not Implemented" + elsif status == 502 + "Bad Gateway" + elsif status == 503 + "Service Unavailable" + elsif status == 504 + "Gateway Time-out" + elsif status == 505 + "HTTP Version not supported" + else + raise "Unknown Statuscode. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1 for more information." + end + # }}} + end +end + +class Cookie + attr_reader :name, :value, :maxage, :path, :domain, :secure, :comment + + # Sets a cookie. Please see below for details of the attributes. + def initialize (name, value = nil, maxage = nil, path = nil, domain = nil, secure = false) + # {{{ + # HTTP headers (Cookies are a HTTP header) can only set, while no content + # is send. So an exception will be raised, when @@allowed is set to false + # and a new cookie has set. + unless defined?(@@allowed) + @@allowed = true + end + unless @@allowed + raise "You can't set cookies after the HTTP headers are send." + end + + unless defined?(@@list) + @@list = [] + end + @@list += [self] + + unless defined?(@@type) + @@type = "netscape" + end + + unless name.class == String + raise TypeError, "The name of a cookie must be a string", caller + end + if value.class.superclass == Integer || value.class == Float + value = value.to_s + elsif value.class != String && value != nil + raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller + end + if maxage.class == Time + maxage = maxage - Time.now + elsif !maxage.class.superclass == Integer || !maxage == nil + raise TypeError, "The maxage date of a cookie must be an Integer or Time object or nil.", caller + end + unless path.class == String || path == nil + raise TypeError, "The path of a cookie must be nil or a string", caller + end + unless domain.class == String || domain == nil + raise TypeError, "The value of a cookie must be nil or a string", caller + end + unless secure == true || secure == false + raise TypeError, "The secure field of a cookie must be true or false", caller + end + + @name, @value, @maxage, @path, @domain, @secure = name, value, maxage, path, domain, secure + @comment = nil + # }}} + end + + # Modifies the value of this cookie. The information you want to store. If the + # value is nil, the cookie will be deleted by the client. + # + # This attribute can be a String, Integer or Float object or nil. + def value=(value) + # {{{ + if value.class.superclass == Integer || value.class == Float + value = value.to_s + elsif value.class != String && value != nil + raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller + end + @value = value + # }}} + end + + # Modifies the maxage of this cookie. This attribute defines the lifetime of + # the cookie, in seconds. A value of 0 means the cookie should be discarded + # imediatly. If it set to nil, the cookie will be deleted when the browser + # will be closed. + # + # Attention: This is different from other implementations like PHP, where you + # gives the seconds since 1/1/1970 0:00:00 GMT. + # + # This attribute must be an Integer or Time object or nil. + def maxage=(maxage) + # {{{ + if maxage.class == Time + maxage = maxage - Time.now + elsif maxage.class.superclass == Integer || !maxage == nil + raise TypeError, "The maxage of a cookie must be an Interger or Time object or nil.", caller + end + @maxage = maxage + # }}} + end + + # Modifies the path value of this cookie. The client will send this cookie + # only, if the requested document is this directory or a subdirectory of it. + # + # The value of the attribute must be a String object or nil. + def path=(path) + # {{{ + unless path.class == String || path == nil + raise TypeError, "The path of a cookie must be nil or a string", caller + end + @path = path + # }}} + end + + # Modifies the domain value of this cookie. The client will send this cookie + # only if it's connected with this domain (or a subdomain, if the first + # character is a dot like in ".ruby-lang.org") + # + # The value of this attribute must be a String or nil. + def domain=(domain) + # {{{ + unless domain.class == String || domain == nil + raise TypeError, "The domain of a cookie must be a String or nil.", caller + end + @domain = domain + # }}} + end + + # Modifies the secure flag of this cookie. If it's true, the client will only + # send this cookie if it is secured connected with us. + # + # The value od this attribute has to be true or false. + def secure=(secure) + # {{{ + unless secure == true || secure == false + raise TypeError, "The secure field of a cookie must be true or false", caller + end + @secure = secure + # }}} + end + + # Modifies the comment value of this cookie. The comment won't be send, if + # type is "netscape". + def comment=(comment) + # {{{ + unless comment.class == String || comment == nil + raise TypeError, "The comment of a cookie must be a string or nil", caller + end + @comment = comment + # }}} + end + + # Changes the type of all cookies. + # Allowed values are RFC2109 and netscape (default). + def Cookie.type=(type) + # {{{ + unless @@allowed + raise "The cookies are allready send, so you can't change the type anymore." + end + unless type.downcase == "rfc2109" && type.downcase == "netscape" + raise "The type of the cookies must be \"RFC2109\" or \"netscape\"." + end + @@type = type; + # }}} + end + + # After sending this message, no cookies can be set or modified. Use it, when + # HTTP-Headers are send. Rweb does this for you. + def Cookie.disallow + # {{{ + @@allowed = false + true + # }}} + end + + # Returns a HTTP header (type String) with all cookies. Rweb does this for + # you. + def Cookie.getHttpHeader + # {{{ + if defined?(@@list) + if @@type == "netscape" + str = "" + @@list.each do |cookie| + if cookie.value == nil + cookie.maxage = 0 + cookie.value = "" + end + # TODO: Name and value should be escaped! + str += "Set-Cookie: #{cookie.name}=#{cookie.value}" + unless cookie.maxage == nil + expire = Time.now + cookie.maxage + expire.gmtime + str += "; Expire=#{expire.strftime("%a, %d-%b-%Y %H:%M:%S %Z")}" + end + unless cookie.domain == nil + str += "; Domain=#{cookie.domain}" + end + unless cookie.path == nil + str += "; Path=#{cookie.path}" + end + if cookie.secure + str += "; Secure" + end + str += "\r\n" + end + return str + else # type == "RFC2109" + str = "Set-Cookie: " + comma = false; + + @@list.each do |cookie| + if cookie.value == nil + cookie.maxage = 0 + cookie.value = "" + end + if comma + str += "," + end + comma = true + + str += "#{cookie.name}=\"#{cookie.value}\"" + unless cookie.maxage == nil + str += "; Max-Age=\"#{cookie.maxage}\"" + end + unless cookie.domain == nil + str += "; Domain=\"#{cookie.domain}\"" + end + unless cookie.path == nil + str += "; Path=\"#{cookie.path}\"" + end + if cookie.secure + str += "; Secure" + end + unless cookie.comment == nil + str += "; Comment=\"#{cookie.comment}\"" + end + str += "; Version=\"1\"" + end + str + end + else + false + end + # }}} + end +end + +require 'strscan' + +module BBCode + DEBUG = true + + use 'encoder', 'tags', 'tagstack', 'smileys' + +=begin + The Parser class takes care of the encoding. + It scans the given BBCode (as plain text), finds tags + and smilies and also makes links of urls in text. + + Normal text is send directly to the encoder. + + If a tag was found, an instance of a Tag subclass is created + to handle the case. + + The @tagstack manages tag nesting and ensures valid HTML. +=end + + class Parser + class Attribute + # flatten and use only one empty_arg + def self.create attr + attr = flatten attr + return @@empty_attr if attr.empty? + new attr + end + + private_class_method :new + + # remove leading and trailing whitespace; concat lines + def self.flatten attr + attr.strip.gsub(/\n/, ' ') + # -> ^ and $ can only match at begin and end now + end + + ATTRIBUTE_SCAN = / + (?!$) # don't match at end + \s* + ( # $1 = key + [^=\s\]"\\]* + (?: + (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? ) + [^=\s\]"\\]* + )* + ) + (?: + = + ( # $2 = value + [^\s\]"\\]* + (?: + (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? ) + [^\s\]"\\]* + )* + )? + )? + \s* + /x + + def self.parse source + source = source.dup + # empty_tag: the tag looks like [... /] + # slice!: this deletes the \s*/] at the end + # \s+ because [url=http://rubybb.org/forum/] is NOT an empty tag. + # In RubyBBCode, you can use [url=http://rubybb.org/forum/ /], and this has to be + # interpreted correctly. + empty_tag = source.sub!(/^:/, '=') or source.slice!(/\/$/) + debug 'PARSE: ' + source.inspect + ' => ' + empty_tag.inspect + #-> we have now an attr that's EITHER empty OR begins and ends with non-whitespace. + + attr = Hash.new + attr[:flags] = [] + source.scan(ATTRIBUTE_SCAN) { |key, value| + if not value + attr[:flags] << unescape(key) + else + next if value.empty? and key.empty? + attr[unescape(key)] = unescape(value) + end + } + debug attr.inspect + + return empty_tag, attr + end + + def self.unescape_char esc + esc[1] + end + + def self.unquote qt + qt[1..-1].chomp('"').gsub(/\\./) { |esc| unescape_char esc } + end + + def self.unescape str + str.gsub(/ (\\.) | (" [^"\\]* (?:\\.[^"\\]*)* "?) /x) { + if $1 + unescape_char $1 + else + unquote $2 + end + } + end + + include Enumerable + def each &block + @args.each(&block) + end + + attr_reader :source, :args, :value + + def initialize source + @source = source + debug 'Attribute#new(%p)' % source + @empty_tag, @attr = Attribute.parse source + @value = @attr[''].to_s + end + + def empty? + self == @@empty_attr + end + + def empty_tag? + @empty_tag + end + + def [] *keys + res = @attr[*keys] + end + + def flags + attr[:flags] + end + + def to_s + @attr + end + + def inspect + 'ATTR[' + @attr.inspect + (@empty_tag ? ' | empty tag' : '') + ']' + end + end + class Attribute + @@empty_attr = new '' + end + end + diff --git a/tests/examplefiles/example.rhtml b/tests/examplefiles/example.rhtml new file mode 100644 index 0000000..041bec1 --- /dev/null +++ b/tests/examplefiles/example.rhtml @@ -0,0 +1,561 @@ +<% @title = 'Moderatoren-Interface' %> + +
+
<%= link_to 'Proben', :controller => '/admin/proben' %>
+
Die angesetzten Proben des Orchesters
+
<%= link_to 'Auftritte', :controller => '/admin/proben' %>
+
Die Auftritte des Orchesters
+ <%- if @valid_user and @valid_user.admin? -%> +
<%= link_to 'Benutzer', :controller => '/admin/user' %>
+
Benutzer organisieren (nur für den Admin)
+ <%- end -%> +
+<% @title = 'Anmeldung' %> + +<%= render :partial => 'user_form', :object => @user %> +<% @title = 'Administrator erstellen' %> + +<%= render :partial => 'user_form', :object => @user %> +<%= form_tag %> + + + + + + + + + + + + +
Name:<%= text_field 'user', 'name' %>
Passwort:<%= password_field 'user', 'password' %>
<%= submit_tag 'Anmelden' %>
+<%= end_form_tag %> +<% @title = 'Neuer Benutzer' -%> +<%= error_messages_for :user %> +<%= render :partial => 'form', :object => @user %> +<%= form_tag %> + + + + + + + + + + + + +
Name:<%= text_field 'user', 'name' %>
Passwort:<%= password_field 'user', 'password' %>
<%= submit_tag 'Anlegen' %>
+<%= end_form_tag %> +<% @title = 'Auftritte' %> + + + <%= render :partial => 'head' %> + <%= render :partial => 'day', :collection => @days %> +
+<% day, auftritte = *day -%> +<% + for auftritt in auftritte +-%> + + + + <%= colorize day.to_s(:dots) if day %> + <% if day and day.wday == 6 %>
Samstag<% end %> + + + <%= colorize auftritt.time %> + + + <%= colorize auftritt.program %> + <%= link_to 'E', :controller => 'admin/auftritte', :action => :edit, :id => auftritt %> + + + <%= colorize(auftritt.place, 'Ort: ') + '
' unless auftritt.place.blank? %> + + + +<% + day = nil + end +-%> + + Datum + Zeit + Programm + Ort + +<% @title = "Besetzung - #{@instrument.name}" %> + +

+<%= pluralize(@members.size, 'Schüler spielt', 'Schüler spielen') %> <%= h @instrument.name %>: +

+ + + <%= render :partial => 'member', :collection => @members %> +
+<% @title = 'Besetzung: %d Mitglieder' % Member.count -%> + + + + +<%= render :partial => 'member', :collection => @members %> +
+<% @title = "Besetzung - Instrument wählen" %> + +
    +<% for instr in @instruments -%> +
  • + <%= link_to h(instr.name), :action => :instrument, :id => instr.name %> + (<%= h instr.members.size %>) +
  • +<% end -%> +
+<% @title = "Besetzung: #{@member.name}" -%> + +
+ +
Instrument / Aufgabe:
+
<%= link_to_instruments_of @member %>
+ +
Geburtstag:
+
<%= h @member.birthday.to_s(:dots) %>
+ +
Adresse:
+
<%= h @member.street %>
<%= h @member.plz %>
+ +
Telefon:
+
<%= h @member.phone %>
+ +
Email:
+
<%= mail_to @member.email, @member.email, :encode => 'javascript' %>
+ +
+ + <%= link_to member.name, :action => :show, :id => member %>: + <%= link_to_instruments_of member %> + + +<% @title = 'Arbeitsgruppen' -%> +

+ Die Arbeitsgruppen sind verantwortlich für die Organisation und Durchführung verschiedenster Aufgaben: +

+ +
    + +
  • Plakate und Konzertkarten +
      +
    • Frau Schraps
    • +
    • Paul-Robert Achcenich
    • +
    • Josefine Dahms
    • +
    +
  • + +
  • Noten
    +
      +
    • Frau Puppe
    • +
    • Theresa Rebin
    • +
    +
  • + +
  • Programme
    +
      +
    • ?
    • +
    +
  • + +
  • Instrumentenstransporte
    +
      +
    • Frau Feldmann
    • +
    • Knut Müller
    • +
    • Patrick Wolter
    • +
    • Alexaner Wolf
    • +
    +
  • + +
  • Internetseite
    +
      +
    • Frau Sternbeck
    • +
    • Uwe Ritzschke
    • +
    • Paul-Robert Achcenich
    • +
    • Knut Müller
    • +
    • Alexander Wolf
    • +
    +
  • + +
+<% @title = 'Chronik' -%> +

+ Das Jugendsinfonieorchester Marzahn-Hellersdorf wurde im Januar 2005 an der + Musikschule Marzahn-Hellersdorf gegründet und gab im Mai 2005 sein erstes + umjubeltes Konzert im FEZ Wuhlheide. Das Orchester umfasst zur Zeit ca. 65 + jugendliche Musiker und soll auf die Größe eines ausgewachsenen + Sinfonieorchesters erweitert werden (80-100 Musiker). +

+ +

+ Als musikalischer Leiter konnte der Dirigent und Echo-Preisträger Jobst + Liebrecht gewonnen werden, der die Musikschule schon aus einer früheren + Zusammenarbeit anlässlich der Kinderoper 'Pollicino' von Hans Werner Henze + kennt. Das Orchester probt wöchentlich. Neben den Tuttiproben finden außerdem + ebenfalls wöchentlich Stimmsatzproben statt, die von Lehrkräften betreut werden. + Das gemeinsame Ziel ist der Aufbau eines leistungsstarken, lebendigen + Klangkörpers, der die Jugendlichen und die Zuhörer ganz neu und direkt für die + Orchestermusik begeistert und diese Musik in den sozialen Brennpunkt Marzahn- + Hellersdorf trägt. +

+ +

+ Im Jahr sind etwa 2-3 Konzertprogramme geplant, mit denen wir in Konzertsälen + auftreten. Das erste Konzert des Jugendsinfonieorchesters Marzahn-Hellersdorf + wurde von DeutschlandRadio Kultur aufgezeichnet und in einer Sendung mit dem + Titel „EINSTAND: Nicht nur auf der Strasse herumhängen” porträtiert. + Wir wollen außerdem vor Ort in Marzahn und Hellersdorf in die Öffentlichkeit + gehen und spielen, um so für die Kultur zu werben und auch weitere Kinder und + Jugendliche für die Musik und fürs Mitmachen zu gewinnen. Durch die Einrichtung + eines zusätzlichen Vororchesters wird längerfristig versucht, die Arbeit auf ein + breites Fundament zu stellen, eine Werkstatt, ein musikalisches Bauhaus zu + gründen. Wenn die Orchesterarbeit erfolgreich angelaufen ist, sollen auch + übergreifende Projekte (Theater, Tanz, Chor) stattfinden. +

+ +

+ Das Orchester will Musik von heute spielen in jedem Sinn, ob es sich um Stücke + aus der sinfonischen Tradition handelt oder um zeitgenössische Musik. Wir kennen + keine Berührungsängste und sind neugierig auf Musik aller Art und möchten diese + Neugierde mit unserem Publikum teilen. +

+<% @title = 'Dirigent - Jobst Liebrecht' -%> +

+ <%= image_tag 'jobstliebrecht.jpg', :alt => 'Jobst Liebrecht', :title => 'Jobst Liebrecht', :class => 'pic_right' %> + Jobst Liebrecht studierte Dirigieren an der Musikhochschule in München und bei Peter Eötvös. Sein spezielles Interesse + für neue Musik führte schnell zur Zusammenarbeit mit renommierten Ensembles auf dem Gebiet wie dem Ensemble Modern, + Frankfurt, dem Klangforum-Ensemble, Wien, dem Ensemble Köln sowie dem Ensemble United Berlin. Aufnahmen entstanden beim + WDR, beim DeutschlandRadio Berlin, beim BR und beim SFB. Er dirigierte u.a. das Rundfunk Sinfonieorchester Berlin, die + Duisburger Philharmoniker und das Münchner Kammerorchester sowie in den Opernhäusern in Halle und Giessen. Tourneen im + Ausland führten ihn nach Argentinien, Georgien, Südkorea und in die USA. +

+ +

+ Zu den Ur- und Erstaufführungen, die er betreut hat, gehören die Opern 'Lunu' von Moritz Eggert, 'Gloria von Jaxtberg' von + HK Gruber sowie in Zusammenarbeit mit dem Regisseur Einar Schleef das Musiktheaterspiel 'Der Golem in Bayreuth' von Ulla + Berkewicz/Lesch Schmidt am Wiener Burgtheater. +

+ +

+ Jobst Liebrecht war mehrere Jahre lang Assistent von Hans Werner Henze und auch immer wieder pädagogisch tätig. Seine + Aufnahme von Henzes Märchenoper 'Pollicino', die als CD bei Wergo erschienen ist, wurde mit dem ECHO-Preis 2004 in der + Sparte 'Klassik für Kinder' ausgezeichnet. +

+ +

+ Als Komponist ist Jobst Liebrecht mit Liedern, Kammermusik sowie Bühnenmusiken an die Öffentlichkeit getreten. +

+<% message, backtrace = session[:boom] -%> +<% @title = 'Fehler in Zeile %d' % [backtrace[/line\s+#(\d+)/,1]] -%> +
+
<%= h message %>
+
+<%= debug backtrace %> +<% cache :action_suffix => (action = params[:action]) do -%> +

+Der Inhalt für die Aktion <%= h action.inspect %> fehlt noch. +

+<% end -%> +<% @title = 'Schulferien Berlin' -%> +

+ Unser Orchester besteht zu einem sehr großen Teil aus Schülern und auch die + Musikschule, der die meisten von uns entstammen, hat in den Schulferien + geschlossen.
+ Deshalb finden innerhalb der Berliner Ferienzeiten keine Proben statt. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Zeitraum200620072008
+ Winter + 30.01. - 03.02. + 05.02. - 10.02. + 04.02. - 09.02.
+ Ostern/Frühjahr + 10.04. - 21.04. + 02.04. - 13.04. + 17.03. - 28.03.
+ Himmelf./Pfingsten + 30.04. / 18.05. + 30.04. / 18.05. + 02.05.
+ Sommer + 06.07. - 19.08. + 12.07. - 25.08. + 17.07. - 30.08.
+ Herbst + 02.10. - 14.10. + 15.10. - 27.10. +
+ Weihnachten + 27.12. - 05.01.07 + 24.12. - 04.01.08 +
+<% @title = 'Termine' -%> + +
    +
  • <%= link_to 'Auftritte', :controller => '/auftritte' %>
  • +
  • <%= link_to 'Schulferien', :controller => '/content', :action => :schulferien %>
  • +
+ + + + <%= tag 'meta', :'http-equiv' => 'content-language', :content => 'de' %> + <%= tag 'meta', :'http-equiv' => 'content-type', :content => 'text/html; charset=UTF-8' %> + + + + + + + + + + + + + JSO<%-if @title-%> - <%= h @title %><%- end -%> + <%= stylesheet_link_tag '/rcss/main' %> + <%#= stylesheet_link_tag 'main' %> + <%= javascript_include_tag 'nospam' %> + <%#= javascript_include_tag :defaults %> + + + + + + + + + + + + + + + + + +
+ <%= image_tag 'JSO-Logo.gif', :alt => 'JSO-Logo' %> + + +
jugendsinfonieorchester
+
+<% if valid_user -%> +
    + +
+<% end -%> +<% cache :controller => 'menu', :action => 'main_menu' do -%> + <%= render_component :controller => 'menu', :action => 'index' %> +<% end -%> +
+<% unless @flash.keys.empty? -%> +
+ <%- for kind, msg in @flash -%> +
<%= h msg %>
+ <%- end -%> +
+<% end -%> +<%= content_tag 'h3', h(@title) if @title %> +<%= @content_for_layout %> +
+ +
+ powered by Ruby on Rails <%= Rails::Info.properties.value_for 'Rails version' %> [<%= h RAILS_ENV[/^./] %>] + <%= image_tag 'css.png', :alt => 'valid CSS', :title => 'valid Cascading Style Sheet', :style => 'display: inline; vertical-align: middle' %> + <%= image_tag 'xhtml11.png', :alt => 'valid XHTML 1.1', :title => 'valid eXtensible Hypertext Markup Language 1.1', :style => 'display: inline; vertical-align: middle' %> +
+
+ + + + +<% @title = 'Übersicht' -%> + +

nächste Probe

+ + <%= render :partial => 'proben/head' %> + <%= render :partial => 'proben/day', :object => @next_probe %> +
+

<%= link_to 'weitere Proben...', :controller => 'proben' %>

+ +

nächster Auftritt

+ + <%= render :partial => 'auftritte/head' %> + <%= render :partial => 'auftritte/day', :object => @next_auftritt %> +
+

<%= link_to 'mehr Auftritte...', :controller => 'auftritte' %>

+
    + <%= category 'Übersicht', home_url %> + <%= subcat 'Wer sind wir?', :wer %> + <%= subcat 'Dirigent' %> + <%= subcat 'Besetzung', url_for(:controller => '/besetzung') %> + <%= subcat 'Repertoire' %> + + <%= category 'Termine' %> + <%= subcat 'Auftritte', url_for(:controller => '/auftritte', :action => :plan) %> + <%= subcat 'Schulferien' %> + + <%= category 'Probenplan', url_for(:controller => '/proben', :action => :plan) %> + + <%= category 'Organisation' %> + <%= subcat 'Orchesterrat' %> + <%= subcat 'Arbeitsgruppen' %> + + <%= category 'Chronik' %> + <%= subcat 'Konzerte' %> + <%= subcat 'Audio' %> + <%= subcat 'Presse' %> + + <%= category 'Links', '#' %> + <%= subcat 'Bilderseite', 'http://musikschule.iden04.de' %> + <%= subcat 'Musikschule', 'http://www.musikschule-marzahn-hellersdorf.de' %> + +

  • + + <%= category 'Kontakt' %> +
+<% @title = 'Probenplan' %> + + + <%= render :partial => 'head' %> + <%= render :partial => 'day', :collection => @days %> +
+ +

+Ort (wenn nicht anders angegeben): Schule am Pappelhof +

+ +<%= render_partial 'raum' %> +<% day, proben = *day -%> +<% + for probe in proben +-%> + + + + <%= colorize day.to_s(:dots) if day %> + <% if day and day.wday == 6 %>
Samstag<% end %> + + + <%= colorize probe.time %> + + + <%= colorize(probe.place, 'Ort: ') + '
' unless probe.place.blank? %> + <%= colorize probe.program %> + <%= link_to 'E', :controller => 'admin/proben', :action => :edit, :id => probe %> + + + <%= h probe.instrumentation %> + + + +<% + day = nil + end +-%> + + Datum + Zeit + Stücke + Besetzung + +

Probenräume

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
WerRaumAdresse
StreicherSchule am Pappelhof
(Raum Nr.)
(Anschrifft Pappelhofschule)
BlechbläserMusikschule Marzahn
(Raum Nr.)
(Anschrifft Musikscule Marzahn)
HolzbläserSchule am Pappelhof
(Raum Nr.)
(Anschrifft Pappelhofschule)
...(Ort)
(Raum Nr.)
(Anschrifft)
diff --git a/tests/examplefiles/example.sh-session b/tests/examplefiles/example.sh-session new file mode 100644 index 0000000..35b81eb --- /dev/null +++ b/tests/examplefiles/example.sh-session @@ -0,0 +1,17 @@ +user@host:~/path$ ls -a +. .. a b c +user@host:~/path$ diff -u a b +--- a 2008-07-26 17:10:07.000000000 -0700 ++++ b 2008-07-26 17:10:10.000000000 -0700 +@@ -1,3 +1,3 @@ + a +-b ++x + c +user@host:~/path$ echo \ +> a +a +user@host:~/path$ su +root@host:~# +sh-3.1$ # on hardy +sh$ # on etch diff --git a/tests/examplefiles/example.weechatlog b/tests/examplefiles/example.weechatlog new file mode 100644 index 0000000..9f03616 --- /dev/null +++ b/tests/examplefiles/example.weechatlog @@ -0,0 +1,9 @@ +**** Beginning of log 2007 Sep 01 00:23:55 **** +2007 Sep 01 00:23:55 --> weechat_user (weechat@localhost.) ist in den Channel &bitlbee gekommen +2007 Sep 01 00:23:55 -=- Modus &bitlbee [+t] durch localhost. +2007 Sep 01 00:23:55 -@- Nicks &bitlbee: [@root @weechat_user] +2007 Sep 01 00:23:55 -=- Channel &bitlbee: 2 Nicks (2 Operatoren, 0 Halb-Operator, 0 Gevoiceter, 0 normal) +2007 Sep 01 00:23:55 -=- Das Topic von &bitlbee lautet: "Welcome to the control channel. Type help for help information." +2007 Sep 01 00:23:55 Welcome to the BitlBee gateway! +2007 Sep 01 00:23:55 +2007 Sep 01 00:23:55 If you've never used BitlBee before, please do read the help information using the help command. Lots of FAQ's are answered there. \ No newline at end of file diff --git a/tests/examplefiles/example.xhtml b/tests/examplefiles/example.xhtml new file mode 100644 index 0000000..a08cf75 --- /dev/null +++ b/tests/examplefiles/example.xhtml @@ -0,0 +1,376 @@ + + + + Error + + + +

Error

+ + + +
Path: #{path}
+
#{CGI.escapeHTML(error.to_s)}
+
+ Reload this page. + Go to the referer or the home page. +
+
+ + In file '#{error.hot_file}' #{error.hot_file =~ /\.xhtml$/ ? '(line numbering is aproximate due to template transformation)' : nil}: +

+ +
#{line}
+ +
#{line}
+ +
+

Stack Trace

+ + + +

Request

+ + +

Response

+ + +

Session

+ + +

+ Powered by Nitro version #{Nitro::Version} + + + + + +

Home > System > #{"%plural%".humanize} > Edit #{"%name%".humanize}

+ + Show editable + #{form_for @obj, :action => "#{base}/save", :cancel => "#{base}/list", :all => true} + + Show all + #{form_for @obj, :action => "#{base}/save", :cancel => "#{base}/list"} + +
+#{form_for(@%name%)} + + +

#{"%plural%".humanize}

+

New #{"%name%".humanize}

+
+ Search #{"%plural%".humanize}:   +
+ + + + + + + + + + + +
#{obj.to_s}#{obj.update_time.stamp(:db)}editdel
+
+ + +

Home > System > #{"%plural%".humanize}

+ New #{"%name%".humanize} +

+

+ Search #{"%plural%".humanize}:   +
+

+ + + + + + + + + + + +
#(obj.to_s)#{obj.update_time.stamp(:db)}editdel
+
+ #{@pager.navigation} +
+
+ + +

Home > System > #{"%plural%".humanize} > New #{"%name%".humanize}

+ + Show editable + #{form_for @obj, :action => "#{base}/save", :cancel => "#{base}/list", :all => true, :enctype => "multipart/form-data"} + + Show all + #{form_for @obj, :action => "#{base}/save", :cancel => "#{base}/list", :enctype => "multipart/form-data"} + +
+ + +

Home > System > #{"%plural%".humanize} > Search for '#@query'

+

+

+ Search #{"%plural%".humanize}:   +
+

+ +

Search method is not implemented for this object

+ + + + + + + + + + + + +
#(obj.to_s)#{obj.update_time.stamp(:db)}editdel
+
+ #{@pager.navigation} +
+ +
+ + +

View %name%

+

List of %plural%

+ + #{@obj.to_yaml} + +
+Access denied + + +

Home > System

+ +

Og managed classes

+ + + + + + + + + + + + + + + + + +
ClassCountCleanupProperties
#{c.name}#{c.count}deletedestroy#{c.properties.values.join(', ')}
+ +

System configuration

+ + + + + + + + + + + + + + + + +
NameValueTypeDescription
#{s.owner}.#{s.name}#{s.value.inspect}#{s.type}#{s.options[:doc]}
+
+ + + + + Test + + + + + + + +hello +Hello #{username} + +how do you feel? + +Here is your Token: #{token} + +
+ +

Questions with Tags: #{@tags.join(" ")}

+ + 0 ?> + + Too many results for that Tag, please reduce the number by using one of the following Tags: + #{cloud_of(@qtags)} + +
+ +

#{q.question}

+

+ + #{excerpt} +

+

#{q.answers.size.to_i} answers

+ +
+
+ #{@qpager.navigation} +
+ +
+

no question with this/these tag(s) found

+

Ask a question here.

+
+ + + 0 ?> +

Tips with Tags: #{@tags.join(" ")}

+ + Too many results for that Tag, please reduce the number by using one of the following Tags: + #{cloud_of(@ttags)} + +
+ +

#{t.title}

+

+ + #{excerpt} +

+ +
+
+ #{@tpager.navigation} +
+ + + 0 ?> +

Tutorials with Tags: #{@tags.join(" ")}

+ + Too many results for that Tag, please reduce the number by using one of the following Tags: + #{cloud_of(@tuttags)} + +
+ +

#{t.title}

+

+ + #{excerpt} +

+ +
+
+ #{@tpager.navigation} +
+ + + + +
+ + + #{t.name} + +
+ +
+ + +
+ + diff --git a/tests/examplefiles/example.xml b/tests/examplefiles/example.xml new file mode 100644 index 0000000..e657e56 --- /dev/null +++ b/tests/examplefiles/example.xml @@ -0,0 +1,1897 @@ + + + + + + abort + abs + abstract + accept + access + aliased + all + and + array + at + begin + body + constant + declare + delay + delta + digits + do + else + elsif + end + entry + exception + exit + for + function + generic + goto + in + is + limited + mod + new + not + null + of + or + others + out + package + pragma + private + procedure + protected + raise + range + rem + record + renames + requeue + return + reverse + separate + subtype + tagged + task + terminate + then + type + until + use + when + while + with + xor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BEGIN + END + if + else + while + do + for + in + continue + break + print + printf + getline + function + return + next + exit + + + ARGC + ARGV + CONVFMT + ENVIRON + FILENAME + FNR + FS + NF + NR + OFMT + OFS + ORS + RS + RSTART + RLENGTH + SUBSEP + + + gsub + index + length + match + split + sprintf + sub + substr + tolower + toupper + atan2 + cos + exp + int + log + rand + sin + sqrt + srand + close + fflush + system + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + break + case + else + esac + exit + export + for + function + in + return + select + then + until + while + . + done + do + elif + fi + if + + + + cp + date + echo + eval + dcop + dcopstart + dcopfind + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + break + case + continue + default + do + else + enum + extern + for + goto + if + inline + return + sizeof + struct + switch + typedef + union + while + + + auto + char + const + double + float + int + long + register + restrict + short + signed + static + unsigned + void + volatile + _Imaginary + _Complex + _Bool + + + FIXME + TODO + ### + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + aaa + access-list + address + alias + arp + async-bootp + banner + boot + bridge + buffers + busy-message + call-history-mib + cdp + chat-script + class-map + clock + cns + config-register + controller + crypto + default + default-value + dialer + dialer-list + dnsix-dmdp + dnsix-nat + downward-compatible-config + enable + end + exception + exit + file + frame-relay + help + hostname + interface + ip + isdn + isdn-mib + kerberos + key + line + logging + login-string + map-class + map-list + memory-size + menu + modemcap + multilink + netbios + no + ntp + partition + policy-map + priority-list + privilege + process-max-time + prompt + queue-list + resume-string + rlogin + rmon + route-map + router + rtr + scheduler + service + snmp-server + sntp + stackmaker + state-machine + subscriber-policy + tacacs-server + template + terminal-queue + tftp-server + time-range + username + virtual-profile + virtual-template + vpdn + vpdn-group + x25 + x29 + + + accounting + accounting-list + accounting-threshold + accounting-transits + address-pool + as-path + audit + auth-proxy + authentication + authorization + bgp-community + bootp + cef + classless + community-list + default-gateway + default-network + dhcp + dhcp-server + domain-list + domain-lookup + domain-name + dvmrp + exec-callback + extcommunity-list + finger + flow-aggregation + flow-cache + flow-export + forward-protocol + ftp + gratuitous-arps + host + host-routing + hp-host + http + icmp + inspect + local + mrm + mroute + msdp + multicast + multicast-routing + name-server + nat + new-model + ospf + password + password-encryption + pgm + pim + port-map + prefix-list + radius + rcmd + reflexive-list + route + routing + rsvp + rtcp + sap + sdr + security + source-route + subnet-zero + tacacs + tcp + tcp-small-servers + telnet + tftp + timestamps + udp-small-servers + vrf + wccp + + + accounting + accounting-list + accounting-threshold + accounting-transits + address-pool + as-path + audit + auth-proxy + authentication + authorization + bgp-community + bootp + cef + classless + community-list + default-gateway + default-network + dhcp + dhcp-server + domain-list + domain-lookup + domain-name + dvmrp + exec-callback + extcommunity-list + finger + flow-aggregation + flow-cache + flow-export + forward-protocol + ftp + gratuitous-arps + host + host-routing + hp-host + http + icmp + inspect + local + mrm + mroute + msdp + multicast + multicast-routing + name-server + nat + new-model + ospf + password + password-encryption + pgm + pim + port-map + prefix-list + radius + rcmd + reflexive-list + route + routing + rsvp + rtcp + sap + sdr + security + source-route + subnet-zero + tacacs + tcp + tcp-small-servers + telnet + tftp + timestamps + udp-small-servers + vrf + wccp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if + else + for + in + while + do + continue + break + with + try + catch + switch + case + new + var + function + return + this + delete + true + false + void + throw + typeof + const + default + + + + + + Anchor + Applet + Area + Array + Boolean + Button + Checkbox + Date + Document + Event + FileUpload + Form + Frame + Function + Hidden + History + Image + Layer + Linke + Location + Math + Navigator + Number + Object + Option + Password + Radio + RegExp + Reset + Screen + Select + String + Submit + Text + Textarea + Window + + + + + + abs + acos + alert + anchor + apply + asin + atan + atan2 + back + blur + call + captureEvents + ceil + charAt + charCodeAt + clearInterval + clearTimeout + click + close + compile + concat + confirm + cos + disableExternalCapture + enableExternalCapture + eval + exec + exp + find + floor + focus + forward + fromCharCode + getDate + getDay + getFullYear + getHours + getMilliseconds + getMinutes + getMonth + getSeconds + getSelection + getTime + getTimezoneOffset + getUTCDate + getUTCDay + getUTCFullYear + getUTCHours + getUTCMilliseconds + getUTCMinutes + getUTCMonth + getUTCSeconds + go + handleEvent + home + indexOf + javaEnabled + join + lastIndexOf + link + load + log + match + max + min + moveAbove + moveBelow + moveBy + moveTo + moveToAbsolute + open + parse + plugins.refresh + pop + pow + preference + print + prompt + push + random + releaseEvents + reload + replace + reset + resizeBy + resizeTo + reverse + round + routeEvent + scrollBy + scrollTo + search + select + setDate + setFullYear + setHours + setInterval + setMilliseconds + setMinutes + setMonth + setSeconds + setTime + setTimeout + setUTCDate + setUTCFullYear + setUTCHours + setUTCMilliseconds + setUTCMinutes + setUTCMonth + setUTCSeconds + shift + sin + slice + sort + splice + split + sqrt + stop + String formatting + submit + substr + substring + taintEnabled + tan + test + toLocaleString + toLowerCase + toSource + toString + toUpperCase + toUTCString + unshift + unwatch + UTC + valueOf + watch + write + writeln + + + + + + break + case + catch + continue + default + do + else + for + function + if + in + return + switch + try + var + while + + + + + + Abs + ACos + ArrayAppend + ArrayAvg + ArrayClear + ArrayDeleteAt + ArrayInsertAt + ArrayIsEmpty + ArrayLen + ArrayMax + ArrayMin + ArrayNew + ArrayPrepend + ArrayResize + ArraySet + ArraySort + ArraySum + ArraySwap + ArrayToList + Asc + ASin + Atn + BitAnd + BitMaskClear + BitMaskRead + BitMaskSet + BitNot + BitOr + BitSHLN + BitSHRN + BitXor + Ceiling + Chr + CJustify + Compare + CompareNoCase + Cos + CreateDate + CreateDateTime + CreateObject + CreateODBCDate + CreateODBCDateTime + CreateODBCTime + CreateTime + CreateTimeSpan + CreateUUID + DateAdd + DateCompare + DateConvert + DateDiff + DateFormat + DatePart + Day + DayOfWeek + DayOfWeekAsString + DayOfYear + DaysInMonth + DaysInYear + DE + DecimalFormat + DecrementValue + Decrypt + DeleteClientVariable + DirectoryExists + DollarFormat + Duplicate + Encrypt + Evaluate + Exp + ExpandPath + FileExists + Find + FindNoCase + FindOneOf + FirstDayOfMonth + Fix + FormatBaseN + GetAuthUser + GetBaseTagData + GetBaseTagList + GetBaseTemplatePath + GetClientVariablesList + GetCurrentTemplatePath + GetDirectoryFromPath + GetException + GetFileFromPath + GetFunctionList + GetHttpRequestData + GetHttpTimeString + GetK2ServerDocCount + GetK2ServerDocCountLimit + GetLocale + GetMetaData + GetMetricData + GetPageContext + GetProfileSections + GetProfileString + GetServiceSettings + GetTempDirectory + GetTempFile + GetTemplatePath + GetTickCount + GetTimeZoneInfo + GetToken + Hash + Hour + HTMLCodeFormat + HTMLEditFormat + IIf + IncrementValue + InputBaseN + Insert + Int + IsArray + IsBinary + IsBoolean + IsCustomFunction + IsDate + IsDebugMode + IsDefined + IsK2ServerABroker + IsK2ServerDocCountExceeded + IsK2ServerOnline + IsLeapYear + IsNumeric + IsNumericDate + IsObject + IsQuery + IsSimpleValue + IsStruct + IsUserInRole + IsWDDX + IsXmlDoc + IsXmlElement + IsXmlRoot + JavaCast + JSStringFormat + LCase + Left + Len + ListAppend + ListChangeDelims + ListContains + ListContainsNoCase + ListDeleteAt + ListFind + ListFindNoCase + ListFirst + ListGetAt + ListInsertAt + ListLast + ListLen + ListPrepend + ListQualify + ListRest + ListSetAt + ListSort + ListToArray + ListValueCount + ListValueCountNoCase + LJustify + Log + Log10 + LSCurrencyFormat + LSDateFormat + LSEuroCurrencyFormat + LSIsCurrency + LSIsDate + LSIsNumeric + LSNumberFormat + LSParseCurrency + LSParseDateTime + LSParseEuroCurrency + LSParseNumber + LSTimeFormat + LTrim + Max + Mid + Min + Minute + Month + MonthAsString + Now + NumberFormat + ParagraphFormat + ParameterExists + ParseDateTime + Pi + PreserveSingleQuotes + Quarter + QueryAddColumn + QueryAddRow + QueryNew + QuerySetCell + QuotedValueList + Rand + Randomize + RandRange + REFind + REFindNoCase + RemoveChars + RepeatString + Replace + ReplaceList + ReplaceNoCase + REReplace + REReplaceNoCase + Reverse + Right + RJustify + Round + RTrim + Second + SetEncoding + SetLocale + SetProfileString + SetVariable + Sgn + Sin + SpanExcluding + SpanIncluding + Sqr + StripCR + StructAppend + StructClear + StructCopy + StructCount + StructDelete + StructFind + StructFindKey + StructFindValue + StructGet + StructInsert + StructIsEmpty + StructKeyArray + StructKeyExists + StructKeyList + StructNew + StructSort + StructUpdate + Tan + TimeFormat + ToBase64 + ToBinary + ToString + Trim + UCase + URLDecode + URLEncodedFormat + URLSessionFormat + Val + ValueList + Week + WriteOutput + XmlChildPos + XmlElemNew + XmlFormat + XmlNew + XmlParse + XmlSearch + XmlTransform + Year + YesNoFormat + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BEGIN + BY + CASE + CLOSE + CONST + DO + ELSE + ELSIF + END + FOR + IF + IMPORT + LOOP + MODULE + NEW + OF + OUT + PROCEDURE + REPEAT + THEN + TO + TYPE + UNTIL + VAR + WHILE + WITH + + + ASSERT + EXIT + HALT + RETURN + + + ANYPTR + ANYREC + ARRAY + BOOLEAN + SHORTCHAR + CHAR + BYTE + SHORTINT + INTEGER + LONGINT + POINTER + RECORD + SHORTREAL + REAL + SET + + + ABSTRACT + EMPTY + EXTENSIBLE + LIMITED + + + ABS + ASH + BITS + CAP + CHR + DEC + ENTIER + EXCL + INC + INCL + LEN + LONG + MAX + MIN + ODD + ORD + SHORT + SIZE + + + FALSE + INF + NIL + TRUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examplefiles/example.yaml b/tests/examplefiles/example.yaml new file mode 100644 index 0000000..9c0ed9d --- /dev/null +++ b/tests/examplefiles/example.yaml @@ -0,0 +1,302 @@ + +# +# Examples from the Preview section of the YAML specification +# (http://yaml.org/spec/1.2/#Preview) +# + +# Sequence of scalars +--- +- Mark McGwire +- Sammy Sosa +- Ken Griffey + +# Mapping scalars to scalars +--- +hr: 65 # Home runs +avg: 0.278 # Batting average +rbi: 147 # Runs Batted In + +# Mapping scalars to sequences +--- +american: + - Boston Red Sox + - Detroit Tigers + - New York Yankees +national: + - New York Mets + - Chicago Cubs + - Atlanta Braves + +# Sequence of mappings +--- +- + name: Mark McGwire + hr: 65 + avg: 0.278 +- + name: Sammy Sosa + hr: 63 + avg: 0.288 + +# Sequence of sequences +--- +- [name , hr, avg ] +- [Mark McGwire, 65, 0.278] +- [Sammy Sosa , 63, 0.288] + +# Mapping of mappings +--- +Mark McGwire: {hr: 65, avg: 0.278} +Sammy Sosa: { + hr: 63, + avg: 0.288 + } + +# Two documents in a stream +--- # Ranking of 1998 home runs +- Mark McGwire +- Sammy Sosa +- Ken Griffey +--- # Team ranking +- Chicago Cubs +- St Louis Cardinals + +# Documents with the end indicator +--- +time: 20:03:20 +player: Sammy Sosa +action: strike (miss) +... +--- +time: 20:03:47 +player: Sammy Sosa +action: grand slam +... + +# Comments +--- +hr: # 1998 hr ranking + - Mark McGwire + - Sammy Sosa +rbi: + # 1998 rbi ranking + - Sammy Sosa + - Ken Griffey + +# Anchors and aliases +--- +hr: + - Mark McGwire + # Following node labeled SS + - &SS Sammy Sosa +rbi: + - *SS # Subsequent occurrence + - Ken Griffey + +# Mapping between sequences +--- +? - Detroit Tigers + - Chicago cubs +: + - 2001-07-23 +? [ New York Yankees, + Atlanta Braves ] +: [ 2001-07-02, 2001-08-12, + 2001-08-14 ] + +# Inline nested mapping +--- +# products purchased +- item : Super Hoop + quantity: 1 +- item : Basketball + quantity: 4 +- item : Big Shoes + quantity: 1 + +# Literal scalars +--- | # ASCII art + \//||\/|| + // || ||__ + +# Folded scalars +--- > + Mark McGwire's + year was crippled + by a knee injury. + +# Preserved indented block in a folded scalar +--- +> + Sammy Sosa completed another + fine season with great stats. + + 63 Home Runs + 0.288 Batting Average + + What a year! + +# Indentation determines scope +--- +name: Mark McGwire +accomplishment: > + Mark set a major league + home run record in 1998. +stats: | + 65 Home Runs + 0.278 Batting Average + +# Quoted scalars +--- +unicode: "Sosa did fine.\u263A" +control: "\b1998\t1999\t2000\n" +hex esc: "\x0d\x0a is \r\n" +single: '"Howdy!" he cried.' +quoted: ' # not a ''comment''.' +tie-fighter: '|\-*-/|' + +# Multi-line flow scalars +--- +plain: + This unquoted scalar + spans many lines. +quoted: "So does this + quoted scalar.\n" + +# Integers +--- +canonical: 12345 +decimal: +12_345 +sexagesimal: 3:25:45 +octal: 014 +hexadecimal: 0xC + +# Floating point +--- +canonical: 1.23015e+3 +exponential: 12.3015e+02 +sexagesimal: 20:30.15 +fixed: 1_230.15 +negative infinity: -.inf +not a number: .NaN + +# Miscellaneous +--- +null: ~ +true: boolean +false: boolean +string: '12345' + +# Timestamps +--- +canonical: 2001-12-15T02:59:43.1Z +iso8601: 2001-12-14t21:59:43.10-05:00 +spaced: 2001-12-14 21:59:43.10 -5 +date: 2002-12-14 + +# Various explicit tags +--- +not-date: !!str 2002-04-28 +picture: !!binary | + R0lGODlhDAAMAIQAAP//9/X + 17unp5WZmZgAAAOfn515eXv + Pz7Y6OjuDg4J+fn5OTk6enp + 56enmleECcgggoBADs= +application specific tag: !something | + The semantics of the tag + above may be different for + different documents. + +# Global tags +%TAG ! tag:clarkevans.com,2002: +--- !shape + # Use the ! handle for presenting + # tag:clarkevans.com,2002:circle +- !circle + center: &ORIGIN {x: 73, y: 129} + radius: 7 +- !line + start: *ORIGIN + finish: { x: 89, y: 102 } +- !label + start: *ORIGIN + color: 0xFFEEBB + text: Pretty vector drawing. + +# Unordered sets +--- !!set +# sets are represented as a +# mapping where each key is +# associated with the empty string +? Mark McGwire +? Sammy Sosa +? Ken Griff + +# Ordered mappings +--- !!omap +# ordered maps are represented as +# a sequence of mappings, with +# each mapping having one key +- Mark McGwire: 65 +- Sammy Sosa: 63 +- Ken Griffy: 58 + +# Full length example +--- ! +invoice: 34843 +date : 2001-01-23 +bill-to: &id001 + given : Chris + family : Dumars + address: + lines: | + 458 Walkman Dr. + Suite #292 + city : Royal Oak + state : MI + postal : 48046 +ship-to: *id001 +product: + - sku : BL394D + quantity : 4 + description : Basketball + price : 450.00 + - sku : BL4438H + quantity : 1 + description : Super Hoop + price : 2392.00 +tax : 251.42 +total: 4443.52 +comments: + Late afternoon is best. + Backup contact is Nancy + Billsmer @ 338-4338. + +# Another full-length example +--- +Time: 2001-11-23 15:01:42 -5 +User: ed +Warning: + This is an error message + for the log file +--- +Time: 2001-11-23 15:02:31 -5 +User: ed +Warning: + A slightly different error + message. +--- +Date: 2001-11-23 15:03:17 -5 +User: ed +Fatal: + Unknown variable "bar" +Stack: + - file: TopClass.py + line: 23 + code: | + x = MoreObject("345\n") + - file: MoreClass.py + line: 58 + code: |- + foo = bar + diff --git a/tests/examplefiles/example2.aspx b/tests/examplefiles/example2.aspx new file mode 100644 index 0000000..52b7c00 --- /dev/null +++ b/tests/examplefiles/example2.aspx @@ -0,0 +1,29 @@ +<%@ Register TagPrefix="Acme" TagName="Message" Src="userctrl2_vb.ascx" %> + + + + + + + +

A Simple User Control w/ Properties

+ +
+ + + +

+ + + + + + + diff --git a/tests/examplefiles/firefox.mak b/tests/examplefiles/firefox.mak new file mode 100644 index 0000000..4dc0f16 --- /dev/null +++ b/tests/examplefiles/firefox.mak @@ -0,0 +1,586 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = . +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +include $(topsrcdir)/build/unix/modules.mk + +ifeq ($(BUILD_MODULES),all) +# +# And now for something completely different... +# Divide the default build into tiers. +# Tiers must be defined on module boundaries +# +SUPPRESS_DEFAULT_RULES = 1 + +default alldep all:: $(SUBMAKEFILES) + $(RM) -rf $(DIST)/sdk + $(RM) -rf $(DIST)/include + $(MAKE) -C config export + $(MAKE) nspr + $(MAKE) ldap + $(MAKE) tier_0 + $(MAKE) tier_1 + $(MAKE) tier_2 + $(MAKE) tier_9 + $(MAKE) tier_50 + $(MAKE) tier_99 + +# Make sure that the existing rulesets work +DIRS = \ + $(tier_0_dirs) \ + $(tier_1_dirs) \ + $(tier_2_dirs) \ + $(tier_9_dirs) \ + $(tier_50_dirs) \ + $(NULL) + +ifdef GC_LEAK_DETECTOR +DIRS += gc/boehm +endif + +DIRS += $(tier_99_dirs) + +# +# tier 0 - base build config dirs +# +tier_0_dirs = \ + config \ + build \ + $(NULL) + +# +# tier 1 - 3rd party individual libraries +# +tier_1_dirs += dbm + +ifndef MOZ_NATIVE_JPEG +tier_1_dirs += jpeg +endif + +ifndef MOZ_NATIVE_ZLIB +tier_1_dirs += modules/zlib +endif + +# Installer needs standalone libjar, hence standalone zlib +ifdef MOZ_INSTALLER +tier_1_dirs += modules/zlib/standalone +endif + +ifdef MOZ_UPDATER +tier_1_dirs += modules/libbz2 +tier_1_dirs += modules/libmar +endif + +ifdef MOZ_SVG_RENDERER_LIBART +tier_1_dirs += other-licenses/libart_lgpl +endif + +# +# tier 2 - base libraries +# +tier_2_dirs = \ + js \ + xpcom \ + $(NULL) + +ifndef MOZ_NO_XPCOM_OBSOLETE +tier_2_dirs += modules/libreg xpcom/obsolete +endif + +ifdef NS_TRACE_MALLOC +tier_2_dirs += tools/trace-malloc/lib +endif + +# +# tier 9 - core components (necko,gecko) +# + +tier_9_dirs += \ + js/src/xpconnect \ + intl \ + db \ + $(NULL) + +ifdef MOZ_STORAGE +tier_9_dirs += storage +endif + +ifdef MOZ_ENABLE_XLIB +tier_9_dirs += gfx/src/xlibrgb widget/src/xlibxtbin +endif + +ifdef MOZ_ENABLE_GTK +tier_9_dirs += widget/src/gtksuperwin widget/src/gtkxtbin +endif + +ifdef MOZ_ENABLE_GTK2 +tier_9_dirs += widget/src/gtkxtbin +endif + +ifdef MOZ_IPCD +tier_9_dirs += ipc/ipcd +endif + +ifdef MOZ_JSDEBUGGER +tier_9_dirs += js/jsd +endif + +tier_9_dirs += \ + modules/libutil \ + netwerk \ + modules/libjar \ + uriloader \ + modules/libpref \ + modules/libimg \ + caps \ + rdf \ + parser/expat \ + parser/xml \ + parser/htmlparser \ + gfx \ + modules/libpr0n \ + sun-java \ + modules/plugin \ + dom \ + view \ + widget \ + content \ + layout \ + xpfe/components/shistory \ + docshell \ + webshell \ + embedding \ + editor \ + xpfe/appshell \ + $(NULL) + +ifdef MOZ_OJI +tier_9_dirs += \ + js/src/liveconnect \ + modules/oji \ + $(NULL) +endif + +ifdef ACCESSIBILITY +tier_9_dirs += accessible +endif + +# +# tier 50 - xpfe & toolkit +# + +ifdef MOZ_XUL +ifdef MOZ_XUL_APP +tier_50_dirs += chrome +else +tier_50_dirs += rdf/chrome +endif +else +tier_50_dirs += embedding/minimo/chromelite +endif + +tier_50_dirs += profile + +# This must preceed xpfe +ifdef MOZ_JPROF +tier_50_dirs += tools/jprof +endif + +ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) +tier_50_dirs += xpfe/bootstrap/appleevents +endif + +tier_50_dirs += \ + xpfe \ + toolkit/components \ + $(NULL) + +ifndef MOZ_XUL_APP +tier_50_dirs += themes +endif + +ifdef MOZ_ENABLE_XREMOTE +tier_50_dirs += widget/src/xremoteclient +endif + +ifdef MOZ_XUL_APP +tier_50_dirs += toolkit +endif + +ifdef MOZ_PHOENIX +#XXXBlake this shell path is a temp hack; toolkit shouldn't depend on browser +tier_50_dirs += browser/components/shell/public +endif + +ifdef MOZ_XPINSTALL +tier_50_dirs += xpinstall +endif + +# JavaXPCOM JNI code is compiled into libXUL +ifdef MOZ_JAVAXPCOM +tier_50_dirs += extensions/java/xpcom/src +endif + +ifdef MOZ_ENABLE_LIBXUL +tier_50_dirs += \ + toolkit/library \ + xpcom/stub \ + $(NULL) +endif + +ifdef NS_TRACE_MALLOC +tier_50_dirs += tools/trace-malloc +endif + +ifdef MOZ_PSM +tier_50_dirs += security/manager +else +tier_50_dirs += security/manager/boot/public security/manager/ssl/public +endif + +ifdef MOZ_LDAP_XPCOM +tier_50_dirs += directory/xpcom +endif + +ifndef MINIMO +ifdef MOZ_XUL_APP +ifdef MOZ_ENABLE_GTK2 +tier_50_dirs += toolkit/components/gnome +endif +endif +endif + +ifdef MOZ_LEAKY +tier_50_dirs += tools/leaky +endif + +ifdef MOZ_MAPINFO +tier_50_dirs += tools/codesighs +endif + +# +# tier 99 - application features +# + +ifdef MOZ_MAIL_NEWS +tier_99_dirs += mailnews +endif + +ifdef MOZ_CALENDAR +tier_99_dirs += calendar +endif + +ifdef MOZ_EXTENSIONS +tier_99_dirs += extensions +endif + +ifdef MOZ_JAVAXPCOM +tier_99_dirs += extensions/java +endif + +# axcontrol +ifeq ($(OS_ARCH),WINNT) +ifndef MOZ_NO_ACTIVEX_SUPPORT +tier_99_dirs += \ + embedding/browser/activex/src/control \ + embedding/browser/activex/src/control_kicker \ + $(NULL) +endif +endif + +# Java Embedding Plugin +ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) +tier_99_dirs += plugin/oji/JEP +endif + +ifneq (,$(filter browser suite,$(MOZ_BUILD_APP))) +tier_99_dirs += xpfe/components/search +endif + +ifdef MOZ_BRANDING_DIRECTORY +tier_99_dirs += $(MOZ_BRANDING_DIRECTORY) +endif + +ifdef MOZ_PHOENIX +tier_99_dirs += browser xpfe/bootstrap/init.d +endif + +ifdef MOZ_XULRUNNER +tier_99_dirs += xulrunner +endif + +ifdef MOZ_COMPOSER +tier_99_dirs += editor/ui +endif + +ifdef MOZ_THUNDERBIRD +tier_99_dirs += mail xpfe/bootstrap/init.d +endif + +ifdef MOZ_STANDALONE_COMPOSER +tier_99_dirs += composer +endif + +ifdef MOZ_SUNBIRD +tier_99_dirs += calendar/sunbird +endif + +ifdef MOZ_SUITE +tier_99_dirs += suite +endif + +ifdef MINIMO +tier_99_dirs += minimo +endif + +ifdef MOZ_XUL_APP +ifdef MOZ_INSTALLER +tier_99_dirs += toolkit/mozapps/installer +endif +else +ifneq (,$(MOZ_XPFE_COMPONENTS)$(MOZ_XUL)) +ifndef MINIMO +tier_99_dirs += xpfe/bootstrap +endif +endif +endif + +ifneq (,$(MOZ_ENABLE_GTK)$(MOZ_ENABLE_GTK2)) +tier_99_dirs += embedding/browser/gtk +endif + +# viewer +ifneq (,$(ENABLE_TESTS)) +ifndef MOZ_ENABLE_LIBXUL +tier_99_dirs += webshell/tests +endif +endif + +# winembed, mfcembed +ifeq ($(OS_ARCH),WINNT) +ifneq (,$(ENABLE_TESTS)$(MOZILLA_OFFICIAL)) +tier_99_dirs += embedding/tests +endif +endif + +# os2embed +ifeq ($(OS_ARCH),OS2) +ifneq (,$(ENABLE_TESTS)$(MOZILLA_OFFICIAL)) +tier_99_dirs += embedding/tests +endif +endif + +ifeq ($(MOZ_BUILD_APP),macbrowser) +tier_99_dirs += \ + embedding/config \ + camino \ + $(NULL) +endif + +# test harnesses +ifdef ENABLE_TESTS +tier_99_dirs += tools/test-harness +endif + +else + +# Standalone build + +DIRS = $(BUILD_MODULE_DIRS) + +# Hack to generate xpidl Makefile +ifneq ($(BUILD_MODULES),all) +ifneq (,$(findstring xpcom, $(BUILD_MODULE_DIRS))) +DIRS := xpcom/typelib $(DIRS) +SUBMAKEFILES := xpcom/typelib/Makefile +endif +endif + +default:: $(SUBMAKEFILES) + $(MAKE) export + $(MAKE) libs + +endif # BUILD_MODULES == all + +STATIC_MAKEFILES := nsprpub directory/c-sdk security/nss + +GARBAGE_DIRS += dist +DIST_GARBAGE = config.cache config.log config.status config-defs.h \ + dependencies.beos config/autoconf.mk config/myrules.mk config/myconfig.mk \ + unallmakefiles mozilla-config.h \ + $(topsrcdir)/.mozconfig.mk $(topsrcdir)/.mozconfig.out + +# Build pseudo-external modules first when export is explicitly called +export:: + $(RM) -rf $(DIST)/sdk + $(MAKE) -C config export + $(MAKE) nspr + $(MAKE) ldap +ifneq ($(BUILD_MODULES),all) +ifneq (,$(findstring xpcom, $(BUILD_MODULE_DIRS))) + $(MAKE) -C xpcom/typelib + $(MAKE) export-idl +endif +endif + +install:: +ifndef MOZ_NATIVE_NSPR + $(MAKE) -C nsprpub real_install DESTDIR=$(DESTDIR) libdir=$(mozappdir) includedir=$(includedir)/nspr + $(RM) -f $(addprefix $(DESTDIR)$(mozappdir)/$(LIB_PREFIX), $(addsuffix .$(LIB_SUFFIX), nspr4 plds4 plc4)) + $(RM) -f $(addprefix $(DESTDIR)$(bindir)/,nspr-config compile-et.pl prerr.properties) +endif +ifdef MOZ_LDAP_XPCOM + $(MAKE) -C directory/c-sdk real_install DESTDIR=$(DESTDIR) libdir=$(mozappdir) includedir=$(includedir)/ldap +endif + +include $(topsrcdir)/config/rules.mk + +# Clean up after pseudo-external modules +clean clobber realclean clobber_all distclean:: +ifndef MOZ_NATIVE_NSPR + $(MAKE) -C nsprpub $@ +endif +ifdef MOZ_LDAP_XPCOM + $(MAKE) -C directory/c-sdk $@ +endif + +# Map mozilla targets to standard automake target +ifdef MOZ_ENABLE_LIBXUL +tier_50: $(addsuffix /Makefile, $(filter-out $(STATIC_MAKEFILES), $($@_dirs))) + @echo "tier_50: $(tier_50_dirs)" + @$(EXIT_ON_ERROR) \ + for d in $(tier_50_dirs); do \ + $(UPDATE_TITLE) \ + if test ! -f $$d/Makefile; then \ + $(PERL) $(AUTOCONF_TOOLS)/make-makefile -t $(topsrcdir) -d $(DEPTH) $(CYGWIN_TOPSRCDIR) $$d/Makefile; \ + fi; \ + $(MAKE) -C $$d export; \ + done ; \ + for d in $(tier_50_dirs); do \ + $(UPDATE_TITLE) \ + $(MAKE) -C $$d libs; \ + done + @echo "Building tools from tier 2/9/50" + @$(EXIT_ON_ERROR) \ + for d in $(tier_2_dirs) $(tier_9_dirs) $(tier_50_dirs); do \ + $(UPDATE_TITLE) \ + $(MAKE) -C $$d tools; \ + done; +endif + +tier_%: + @echo "$@: $($@_dirs)" + @$(EXIT_ON_ERROR) \ + for d in $($@_dirs); do \ + $(UPDATE_TITLE) \ + if test ! -f $$d/Makefile; then \ + $(PERL) $(AUTOCONF_TOOLS)/make-makefile -t $(topsrcdir) -d $(DEPTH) $(CYGWIN_TOPSRCDIR) $$d/Makefile; \ + fi; \ + $(MAKE) -C $$d export; \ + done ; \ + for d in $($@_dirs); do $(UPDATE_TITLE) \ + $(MAKE) -C $$d libs; \ + done + +# +# Individual modules +# +boehm: +ifdef GC_LEAK_DETECTOR + $(MAKE) -C gc/boehm +endif + +nspr: boehm +ifndef MOZ_NATIVE_NSPR + $(MAKE) -C nsprpub +endif + +ldap: +ifdef MOZ_LDAP_XPCOM + $(MAKE) -C directory/c-sdk +endif + +distclean:: + cat unallmakefiles | $(XARGS) rm -f + rm -f unallmakefiles $(DIST_GARBAGE) + +ifeq ($(OS_ARCH),WINNT) +rebase: +ifdef MOZILLA_OFFICIAL + echo rebasing $(DIST) + /bin/find $(DIST) -name "*.dll" > rebase.lst + rebase -b 60000000 -R . -G rebase.lst + rm rebase.lst +endif + +splitsymbols: +ifdef MOZILLA_OFFICIAL +ifdef MOZ_DEBUG_SYMBOLS + echo finding pdb files + mkdir -p $(DIST)/$(BUILDID) + -cp `/bin/find . -path "./dist" -prune -o -name "*.dll" | sed "s/\.dll$$/\.pdb/" | xargs` $(DIST)/$(BUILDID) + -cp `/bin/find . -path "./dist" -prune -o -name "*.exe" | sed "s/\.exe$$/\.pdb/" | xargs` $(DIST)/$(BUILDID) + -cp `/bin/find . -path "./dist" -prune -o -name "*.EXE" | sed "s/\.EXE$$/\.pdb/" | xargs` $(DIST)/$(BUILDID) +endif # MOZ_DEBUG_SYMBOLS +ifdef MOZ_PROFILE + echo splitting symbols out of binaries + /bin/find $(DIST) -name "*.dll" -exec splitsym {} \; + /bin/find $(DIST) -name "*.exe" -exec splitsym {} \; + /bin/find $(DIST) -name "*.EXE" -exec splitsym {} \; + mkdir -p $(DIST)/$(BUILDID) + /bin/find $(DIST) -name "*.dbg" -exec mv {} $(DIST)/$(BUILDID) \; +endif # MOZ_PROFILE +endif # MOZILLA_OFFICIAL + +signnss: +ifdef MOZILLA_OFFICIAL + echo signing NSS libs + cd $(DIST)/bin; ./shlibsign.exe -v -i softokn3.dll + cd $(DIST)/bin; ./shlibsign.exe -v -i freebl3.dll +endif # MOZILLA_OFFICIAL + +BUILDID = $(shell cat $(DEPTH)/config/build_number) +deliver: splitsymbols rebase signnss + +endif # WINNT + diff --git a/tests/examplefiles/format.ml b/tests/examplefiles/format.ml new file mode 100644 index 0000000..49b4067 --- /dev/null +++ b/tests/examplefiles/format.ml @@ -0,0 +1,1213 @@ +(***********************************************************************) +(* *) +(* Objective Caml *) +(* *) +(* Pierre Weis, projet Cristal, INRIA Rocquencourt *) +(* *) +(* Copyright 1996 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU Library General Public License, with *) +(* the special exception on linking described in file ../LICENSE. *) +(* *) +(***********************************************************************) + +(* $Id: format.ml,v 1.65 2005/09/26 10:13:08 weis Exp $ *) + +(************************************************************** + + Data structures definitions. + + **************************************************************) + +type size;; + +external size_of_int : int -> size = "%identity";; +external int_of_size : size -> int = "%identity";; + +(* Tokens are one of the following : *) + +type pp_token = +| Pp_text of string (* normal text *) +| Pp_break of int * int (* complete break *) +| Pp_tbreak of int * int (* go to next tabulation *) +| Pp_stab (* set a tabulation *) +| Pp_begin of int * block_type (* beginning of a block *) +| Pp_end (* end of a block *) +| Pp_tbegin of tblock (* beginning of a tabulation block *) +| Pp_tend (* end of a tabulation block *) +| Pp_newline (* to force a newline inside a block *) +| Pp_if_newline (* to do something only if this very + line has been broken *) +| Pp_open_tag of string (* opening a tag name *) +| Pp_close_tag (* closing the most recently opened tag *) + +and tag = string + +and block_type = +| Pp_hbox (* Horizontal block no line breaking *) +| Pp_vbox (* Vertical block each break leads to a new line *) +| Pp_hvbox (* Horizontal-vertical block: same as vbox, except if this block + is small enough to fit on a single line *) +| Pp_hovbox (* Horizontal or Vertical block: breaks lead to new line + only when necessary to print the content of the block *) +| Pp_box (* Horizontal or Indent block: breaks lead to new line + only when necessary to print the content of the block, or + when it leads to a new indentation of the current line *) +| Pp_fits (* Internal usage: when a block fits on a single line *) + +and tblock = Pp_tbox of int list ref (* Tabulation box *) +;; + +(* The Queue: + contains all formatting elements. + elements are tuples (size, token, length), where + size is set when the size of the block is known + len is the declared length of the token. *) +type pp_queue_elem = { + mutable elem_size : size; token : pp_token; length : int +};; + +(* Scan stack: + each element is (left_total, queue element) where left_total + is the value of pp_left_total when the element has been enqueued. *) +type pp_scan_elem = Scan_elem of int * pp_queue_elem;; + +(* Formatting stack: + used to break the lines while printing tokens. + The formatting stack contains the description of + the currently active blocks. *) +type pp_format_elem = Format_elem of block_type * int;; + +(* General purpose queues, used in the formatter. *) +type 'a queue_elem = | Nil | Cons of 'a queue_cell +and 'a queue_cell = {mutable head : 'a; mutable tail : 'a queue_elem};; + +type 'a queue = { + mutable insert : 'a queue_elem; + mutable body : 'a queue_elem +};; + +(* The formatter specific tag handling functions. *) +type formatter_tag_functions = { + mark_open_tag : tag -> string; + mark_close_tag : tag -> string; + print_open_tag : tag -> unit; + print_close_tag : tag -> unit; + +};; + +(* A formatter with all its machinery. *) +type formatter = { + mutable pp_scan_stack : pp_scan_elem list; + mutable pp_format_stack : pp_format_elem list; + mutable pp_tbox_stack : tblock list; + mutable pp_tag_stack : tag list; + mutable pp_mark_stack : tag list; + (* Global variables: default initialization is + set_margin 78 + set_min_space_left 0. *) + (* Value of right margin. *) + mutable pp_margin : int; + (* Minimal space left before margin, when opening a block. *) + mutable pp_min_space_left : int; + (* Maximum value of indentation: + no blocks can be opened further. *) + mutable pp_max_indent : int; + (* Space remaining on the current line. *) + mutable pp_space_left : int; + (* Current value of indentation. *) + mutable pp_current_indent : int; + (* True when the line has been broken by the pretty-printer. *) + mutable pp_is_new_line : bool; + (* Total width of tokens already printed. *) + mutable pp_left_total : int; + (* Total width of tokens ever put in queue. *) + mutable pp_right_total : int; + (* Current number of opened blocks. *) + mutable pp_curr_depth : int; + (* Maximum number of blocks which can be simultaneously opened. *) + mutable pp_max_boxes : int; + (* Ellipsis string. *) + mutable pp_ellipsis : string; + (* Output function. *) + mutable pp_output_function : string -> int -> int -> unit; + (* Flushing function. *) + mutable pp_flush_function : unit -> unit; + (* Output of new lines. *) + mutable pp_output_newline : unit -> unit; + (* Output of indentation spaces. *) + mutable pp_output_spaces : int -> unit; + (* Are tags printed ? *) + mutable pp_print_tags : bool; + (* Are tags marked ? *) + mutable pp_mark_tags : bool; + (* Find opening and closing markers of tags. *) + mutable pp_mark_open_tag : tag -> string; + mutable pp_mark_close_tag : tag -> string; + mutable pp_print_open_tag : tag -> unit; + mutable pp_print_close_tag : tag -> unit; + (* The pretty-printer queue. *) + mutable pp_queue : pp_queue_elem queue +};; + +(************************************************************** + + Auxilliaries and basic functions. + + **************************************************************) + + +(* Queues auxilliaries. *) +let make_queue () = {insert = Nil; body = Nil};; + +let clear_queue q = q.insert <- Nil; q.body <- Nil;; + +let add_queue x q = + let c = Cons {head = x; tail = Nil} in + match q with + | {insert = Cons cell} -> q.insert <- c; cell.tail <- c + (* Invariant: when insert is Nil body should be Nil. *) + | _ -> q.insert <- c; q.body <- c;; + +exception Empty_queue;; + +let peek_queue = function + | {body = Cons {head = x}} -> x + | _ -> raise Empty_queue;; + +let take_queue = function + | {body = Cons {head = x; tail = tl}} as q -> + q.body <- tl; + if tl = Nil then q.insert <- Nil; (* Maintain the invariant. *) + x + | _ -> raise Empty_queue;; + +(* Enter a token in the pretty-printer queue. *) +let pp_enqueue state ({length = len} as token) = + state.pp_right_total <- state.pp_right_total + len; + add_queue token state.pp_queue;; + +let pp_clear_queue state = + state.pp_left_total <- 1; state.pp_right_total <- 1; + clear_queue state.pp_queue;; + +(* Pp_infinity: large value for default tokens size. + + Pp_infinity is documented as being greater than 1e10; to avoid + confusion about the word ``greater'', we choose pp_infinity greater + than 1e10 + 1; for correct handling of tests in the algorithm, + pp_infinity must be even one more than 1e10 + 1; let's stand on the + safe side by choosing 1.e10+10. + + Pp_infinity could probably be 1073741823 that is 2^30 - 1, that is + the minimal upper bound for integers; now that max_int is defined, + this limit could also be defined as max_int - 1. + + However, before setting pp_infinity to something around max_int, we + must carefully double-check all the integer arithmetic operations + that involve pp_infinity, since any overflow would wreck havoc the + pretty-printing algorithm's invariants. Given that this arithmetic + correctness check is difficult and error prone and given that 1e10 + + 1 is in practice large enough, there is no need to attempt to set + pp_infinity to the theoretically maximum limit. Is it not worth the + burden ! *) + +let pp_infinity = 1000000010;; + +(* Output functions for the formatter. *) +let pp_output_string state s = state.pp_output_function s 0 (String.length s) +and pp_output_newline state = state.pp_output_newline ();; + +let pp_display_blanks state n = state.pp_output_spaces n;; + +(* To format a break, indenting a new line. *) +let break_new_line state offset width = + pp_output_newline state; + state.pp_is_new_line <- true; + let indent = state.pp_margin - width + offset in + (* Don't indent more than pp_max_indent. *) + let real_indent = min state.pp_max_indent indent in + state.pp_current_indent <- real_indent; + state.pp_space_left <- state.pp_margin - state.pp_current_indent; + pp_display_blanks state state.pp_current_indent;; + +(* To force a line break inside a block: no offset is added. *) +let break_line state width = break_new_line state 0 width;; + +(* To format a break that fits on the current line. *) +let break_same_line state width = + state.pp_space_left <- state.pp_space_left - width; + pp_display_blanks state width;; + +(* To indent no more than pp_max_indent, if one tries to open a block + beyond pp_max_indent, then the block is rejected on the left + by simulating a break. *) +let pp_force_break_line state = + match state.pp_format_stack with + | Format_elem (bl_ty, width) :: _ -> + if width > state.pp_space_left then + (match bl_ty with + | Pp_fits -> () | Pp_hbox -> () | _ -> break_line state width) + | _ -> pp_output_newline state;; + +(* To skip a token, if the previous line has been broken. *) +let pp_skip_token state = + (* When calling pp_skip_token the queue cannot be empty. *) + match take_queue state.pp_queue with + {elem_size = size; length = len} -> + state.pp_left_total <- state.pp_left_total - len; + state.pp_space_left <- state.pp_space_left + int_of_size size;; + +(************************************************************** + + The main pretting printing functions. + + **************************************************************) + +(* To format a token. *) +let format_pp_token state size = function + + | Pp_text s -> + state.pp_space_left <- state.pp_space_left - size; + pp_output_string state s; + state.pp_is_new_line <- false + + | Pp_begin (off, ty) -> + let insertion_point = state.pp_margin - state.pp_space_left in + if insertion_point > state.pp_max_indent then + (* can't open a block right there. *) + begin pp_force_break_line state end; + let offset = state.pp_space_left - off in + let bl_type = + begin match ty with + | Pp_vbox -> Pp_vbox + | _ -> if size > state.pp_space_left then ty else Pp_fits + end in + state.pp_format_stack <- + Format_elem (bl_type, offset) :: state.pp_format_stack + + | Pp_end -> + begin match state.pp_format_stack with + | x :: (y :: l as ls) -> state.pp_format_stack <- ls + | _ -> () (* No more block to close. *) + end + + | Pp_tbegin (Pp_tbox _ as tbox) -> + state.pp_tbox_stack <- tbox :: state.pp_tbox_stack + + | Pp_tend -> + begin match state.pp_tbox_stack with + | x :: ls -> state.pp_tbox_stack <- ls + | _ -> () (* No more tabulation block to close. *) + end + + | Pp_stab -> + begin match state.pp_tbox_stack with + | Pp_tbox tabs :: _ -> + let rec add_tab n = function + | [] -> [n] + | x :: l as ls -> if n < x then n :: ls else x :: add_tab n l in + tabs := add_tab (state.pp_margin - state.pp_space_left) !tabs + | _ -> () (* No opened tabulation block. *) + end + + | Pp_tbreak (n, off) -> + let insertion_point = state.pp_margin - state.pp_space_left in + begin match state.pp_tbox_stack with + | Pp_tbox tabs :: _ -> + let rec find n = function + | x :: l -> if x >= n then x else find n l + | [] -> raise Not_found in + let tab = + match !tabs with + | x :: l -> + begin try find insertion_point !tabs with Not_found -> x end + | _ -> insertion_point in + let offset = tab - insertion_point in + if offset >= 0 then break_same_line state (offset + n) else + break_new_line state (tab + off) state.pp_margin + | _ -> () (* No opened tabulation block. *) + end + + | Pp_newline -> + begin match state.pp_format_stack with + | Format_elem (_, width) :: _ -> break_line state width + | _ -> pp_output_newline state + end + + | Pp_if_newline -> + if state.pp_current_indent != state.pp_margin - state.pp_space_left + then pp_skip_token state + + | Pp_break (n, off) -> + begin match state.pp_format_stack with + | Format_elem (ty, width) :: _ -> + begin match ty with + | Pp_hovbox -> + if size > state.pp_space_left + then break_new_line state off width + else break_same_line state n + | Pp_box -> + (* Have the line just been broken here ? *) + if state.pp_is_new_line then break_same_line state n else + if size > state.pp_space_left + then break_new_line state off width else + (* break the line here leads to new indentation ? *) + if state.pp_current_indent > state.pp_margin - width + off + then break_new_line state off width + else break_same_line state n + | Pp_hvbox -> break_new_line state off width + | Pp_fits -> break_same_line state n + | Pp_vbox -> break_new_line state off width + | Pp_hbox -> break_same_line state n + end + | _ -> () (* No opened block. *) + end + + | Pp_open_tag tag_name -> + let marker = state.pp_mark_open_tag tag_name in + pp_output_string state marker; + state.pp_mark_stack <- tag_name :: state.pp_mark_stack + + | Pp_close_tag -> + begin match state.pp_mark_stack with + | tag_name :: tags -> + let marker = state.pp_mark_close_tag tag_name in + pp_output_string state marker; + state.pp_mark_stack <- tags + | _ -> () (* No more tag to close. *) + end;; + +(* Print if token size is known or printing is delayed. + Size is known when not negative. + Printing is delayed when the text waiting in the queue requires + more room to format than exists on the current line. *) +let rec advance_left state = + try + match peek_queue state.pp_queue with + {elem_size = size; token = tok; length = len} -> + let size = int_of_size size in + if not + (size < 0 && + (state.pp_right_total - state.pp_left_total < state.pp_space_left)) + then begin + ignore(take_queue state.pp_queue); + format_pp_token state (if size < 0 then pp_infinity else size) tok; + state.pp_left_total <- len + state.pp_left_total; + advance_left state + end + with Empty_queue -> ();; + +let enqueue_advance state tok = pp_enqueue state tok; advance_left state;; + +(* To enqueue a string : try to advance. *) +let make_queue_elem size tok len = + {elem_size = size; token = tok; length = len};; + +let enqueue_string_as state size s = + let len = int_of_size size in + enqueue_advance state (make_queue_elem size (Pp_text s) len);; + +let enqueue_string state s = + let len = String.length s in + enqueue_string_as state (size_of_int len) s;; + +(* Routines for scan stack + determine sizes of blocks. *) + +(* The scan_stack is never empty. *) +let scan_stack_bottom = + let q_elem = make_queue_elem (size_of_int (-1)) (Pp_text "") 0 in + [Scan_elem (-1, q_elem)];; + +(* Set size of blocks on scan stack: + if ty = true then size of break is set else size of block is set; + in each case pp_scan_stack is popped. *) +let clear_scan_stack state = state.pp_scan_stack <- scan_stack_bottom;; + +(* Pattern matching on scan stack is exhaustive, + since scan_stack is never empty. + Pattern matching on token in scan stack is also exhaustive, + since scan_push is used on breaks and opening of boxes. *) +let set_size state ty = + match state.pp_scan_stack with + | Scan_elem + (left_tot, + ({elem_size = size; token = tok} as queue_elem)) :: t -> + let size = int_of_size size in + (* test if scan stack contains any data that is not obsolete. *) + if left_tot < state.pp_left_total then clear_scan_stack state else + begin match tok with + | Pp_break (_, _) | Pp_tbreak (_, _) -> + if ty then + begin + queue_elem.elem_size <- size_of_int (state.pp_right_total + size); + state.pp_scan_stack <- t + end + | Pp_begin (_, _) -> + if not ty then + begin + queue_elem.elem_size <- size_of_int (state.pp_right_total + size); + state.pp_scan_stack <- t + end + | _ -> () (* scan_push is only used for breaks and boxes. *) + end + | _ -> () (* scan_stack is never empty. *);; + +(* Push a token on scan stack. If b is true set_size is called. *) +let scan_push state b tok = + pp_enqueue state tok; + if b then set_size state true; + state.pp_scan_stack <- + Scan_elem (state.pp_right_total, tok) :: state.pp_scan_stack;; + +(* To open a new block : + the user may set the depth bound pp_max_boxes + any text nested deeper is printed as the ellipsis string. *) +let pp_open_box_gen state indent br_ty = + state.pp_curr_depth <- state.pp_curr_depth + 1; + if state.pp_curr_depth < state.pp_max_boxes then + let elem = + make_queue_elem + (size_of_int (- state.pp_right_total)) + (Pp_begin (indent, br_ty)) + 0 in + scan_push state false elem else + if state.pp_curr_depth = state.pp_max_boxes + then enqueue_string state state.pp_ellipsis;; + +(* The box which is always opened. *) +let pp_open_sys_box state = pp_open_box_gen state 0 Pp_hovbox;; + +(* Close a block, setting sizes of its subblocks. *) +let pp_close_box state () = + if state.pp_curr_depth > 1 then + begin + if state.pp_curr_depth < state.pp_max_boxes then + begin + pp_enqueue state + {elem_size = size_of_int 0; token = Pp_end; length = 0}; + set_size state true; set_size state false + end; + state.pp_curr_depth <- state.pp_curr_depth - 1; + end;; + +(* Open a tag, pushing it on the tag stack. *) +let pp_open_tag state tag_name = + if state.pp_print_tags then begin + state.pp_tag_stack <- tag_name :: state.pp_tag_stack; + state.pp_print_open_tag tag_name end; + if state.pp_mark_tags then + pp_enqueue state + {elem_size = size_of_int 0; token = Pp_open_tag tag_name; length = 0};; + +(* Close a tag, popping it from the tag stack. *) +let pp_close_tag state () = + if state.pp_mark_tags then + pp_enqueue state + {elem_size = size_of_int 0; token = Pp_close_tag; length = 0}; + if state.pp_print_tags then + begin match state.pp_tag_stack with + | tag_name :: tags -> + state.pp_print_close_tag tag_name; + state.pp_tag_stack <- tags + | _ -> () (* No more tag to close. *) + end;; + +let pp_set_print_tags state b = state.pp_print_tags <- b;; +let pp_set_mark_tags state b = state.pp_mark_tags <- b;; +let pp_get_print_tags state () = state.pp_print_tags;; +let pp_get_mark_tags state () = state.pp_mark_tags;; +let pp_set_tags state b = pp_set_print_tags state b; pp_set_mark_tags state b;; + +let pp_get_formatter_tag_functions state () = { + mark_open_tag = state.pp_mark_open_tag; + mark_close_tag = state.pp_mark_close_tag; + print_open_tag = state.pp_print_open_tag; + print_close_tag = state.pp_print_close_tag; +};; + +let pp_set_formatter_tag_functions state { + mark_open_tag = mot; + mark_close_tag = mct; + print_open_tag = pot; + print_close_tag = pct; + } = + state.pp_mark_open_tag <- mot; + state.pp_mark_close_tag <- mct; + state.pp_print_open_tag <- pot; + state.pp_print_close_tag <- pct;; + +(* Initialize pretty-printer. *) +let pp_rinit state = + pp_clear_queue state; + clear_scan_stack state; + state.pp_format_stack <- []; + state.pp_tbox_stack <- []; + state.pp_tag_stack <- []; + state.pp_mark_stack <- []; + state.pp_current_indent <- 0; + state.pp_curr_depth <- 0; + state.pp_space_left <- state.pp_margin; + pp_open_sys_box state;; + +(* Flushing pretty-printer queue. *) +let pp_flush_queue state b = + while state.pp_curr_depth > 1 do + pp_close_box state () + done; + state.pp_right_total <- pp_infinity; + advance_left state; + if b then pp_output_newline state; + pp_rinit state;; + +(************************************************************** + + Procedures to format objects, and use boxes + + **************************************************************) + +(* To format a string. *) +let pp_print_as_size state size s = + if state.pp_curr_depth < state.pp_max_boxes + then enqueue_string_as state size s;; + +let pp_print_as state isize s = + pp_print_as_size state (size_of_int isize) s;; + +let pp_print_string state s = + pp_print_as state (String.length s) s;; + +(* To format an integer. *) +let pp_print_int state i = pp_print_string state (string_of_int i);; + +(* To format a float. *) +let pp_print_float state f = pp_print_string state (string_of_float f);; + +(* To format a boolean. *) +let pp_print_bool state b = pp_print_string state (string_of_bool b);; + +(* To format a char. *) +let pp_print_char state c = + let s = String.create 1 in + s.[0] <- c; + pp_print_as state 1 s;; + +(* Opening boxes. *) +let pp_open_hbox state () = pp_open_box_gen state 0 Pp_hbox +and pp_open_vbox state indent = pp_open_box_gen state indent Pp_vbox + +and pp_open_hvbox state indent = pp_open_box_gen state indent Pp_hvbox +and pp_open_hovbox state indent = pp_open_box_gen state indent Pp_hovbox +and pp_open_box state indent = pp_open_box_gen state indent Pp_box;; + +(* Print a new line after printing all queued text + (same for print_flush but without a newline). *) +let pp_print_newline state () = + pp_flush_queue state true; state.pp_flush_function () +and pp_print_flush state () = + pp_flush_queue state false; state.pp_flush_function ();; + +(* To get a newline when one does not want to close the current block. *) +let pp_force_newline state () = + if state.pp_curr_depth < state.pp_max_boxes then + enqueue_advance state (make_queue_elem (size_of_int 0) Pp_newline 0);; + +(* To format something if the line has just been broken. *) +let pp_print_if_newline state () = + if state.pp_curr_depth < state.pp_max_boxes then + enqueue_advance state (make_queue_elem (size_of_int 0) Pp_if_newline 0);; + +(* Breaks: indicate where a block may be broken. + If line is broken then offset is added to the indentation of the current + block else (the value of) width blanks are printed. + To do (?) : add a maximum width and offset value. *) +let pp_print_break state width offset = + if state.pp_curr_depth < state.pp_max_boxes then + let elem = + make_queue_elem + (size_of_int (- state.pp_right_total)) + (Pp_break (width, offset)) + width in + scan_push state true elem;; + +let pp_print_space state () = pp_print_break state 1 0 +and pp_print_cut state () = pp_print_break state 0 0;; + +(* Tabulation boxes. *) +let pp_open_tbox state () = + state.pp_curr_depth <- state.pp_curr_depth + 1; + if state.pp_curr_depth < state.pp_max_boxes then + let elem = + make_queue_elem (size_of_int 0) (Pp_tbegin (Pp_tbox (ref []))) 0 in + enqueue_advance state elem;; + +(* Close a tabulation block. *) +let pp_close_tbox state () = + if state.pp_curr_depth > 1 then begin + if state.pp_curr_depth < state.pp_max_boxes then + let elem = make_queue_elem (size_of_int 0) Pp_tend 0 in + enqueue_advance state elem; + state.pp_curr_depth <- state.pp_curr_depth - 1 end;; + +(* Print a tabulation break. *) +let pp_print_tbreak state width offset = + if state.pp_curr_depth < state.pp_max_boxes then + let elem = + make_queue_elem + (size_of_int (- state.pp_right_total)) + (Pp_tbreak (width, offset)) + width in + scan_push state true elem;; + +let pp_print_tab state () = pp_print_tbreak state 0 0;; + +let pp_set_tab state () = + if state.pp_curr_depth < state.pp_max_boxes then + let elem = + make_queue_elem (size_of_int 0) Pp_stab 0 in + enqueue_advance state elem;; + +(************************************************************** + + Procedures to control the pretty-printers + + **************************************************************) + +(* Fit max_boxes. *) +let pp_set_max_boxes state n = if n > 1 then state.pp_max_boxes <- n;; + +(* To know the current maximum number of boxes allowed. *) +let pp_get_max_boxes state () = state.pp_max_boxes;; + +let pp_over_max_boxes state () = state.pp_curr_depth = state.pp_max_boxes;; + +(* Ellipsis. *) +let pp_set_ellipsis_text state s = state.pp_ellipsis <- s +and pp_get_ellipsis_text state () = state.pp_ellipsis;; + +(* To set the margin of pretty-printer. *) +let pp_limit n = + if n < pp_infinity then n else pred pp_infinity;; + +let pp_set_min_space_left state n = + if n >= 1 then + let n = pp_limit n in + state.pp_min_space_left <- n; + state.pp_max_indent <- state.pp_margin - state.pp_min_space_left; + pp_rinit state;; + +(* Initially, we have : + pp_max_indent = pp_margin - pp_min_space_left, and + pp_space_left = pp_margin. *) +let pp_set_max_indent state n = + pp_set_min_space_left state (state.pp_margin - n);; +let pp_get_max_indent state () = state.pp_max_indent;; + +let pp_set_margin state n = + if n >= 1 then + let n = pp_limit n in + state.pp_margin <- n; + let new_max_indent = + (* Try to maintain max_indent to its actual value. *) + if state.pp_max_indent <= state.pp_margin + then state.pp_max_indent else + (* If possible maintain pp_min_space_left to its actual value, + if this leads to a too small max_indent, take half of the + new margin, if it is greater than 1. *) + max (max (state.pp_margin - state.pp_min_space_left) + (state.pp_margin / 2)) 1 in + (* Rebuild invariants. *) + pp_set_max_indent state new_max_indent;; + +let pp_get_margin state () = state.pp_margin;; + +let pp_set_formatter_output_functions state f g = + state.pp_output_function <- f; state.pp_flush_function <- g;; +let pp_get_formatter_output_functions state () = + (state.pp_output_function, state.pp_flush_function);; + +let pp_set_all_formatter_output_functions state + ~out:f ~flush:g ~newline:h ~spaces:i = + pp_set_formatter_output_functions state f g; + state.pp_output_newline <- (function () -> h ()); + state.pp_output_spaces <- (function n -> i n);; +let pp_get_all_formatter_output_functions state () = + (state.pp_output_function, state.pp_flush_function, + state.pp_output_newline, state.pp_output_spaces);; + +let pp_set_formatter_out_channel state os = + state.pp_output_function <- output os; + state.pp_flush_function <- (fun () -> flush os);; + +(************************************************************** + + Creation of specific formatters + + **************************************************************) + +let default_pp_mark_open_tag s = "<" ^ s ^ ">";; +let default_pp_mark_close_tag s = "";; + +let default_pp_print_open_tag s = ();; +let default_pp_print_close_tag = default_pp_print_open_tag;; + +let pp_make_formatter f g h i = + (* The initial state of the formatter contains a dummy box. *) + let pp_q = make_queue () in + let sys_tok = + make_queue_elem (size_of_int (-1)) (Pp_begin (0, Pp_hovbox)) 0 in + add_queue sys_tok pp_q; + let sys_scan_stack = + (Scan_elem (1, sys_tok)) :: scan_stack_bottom in + {pp_scan_stack = sys_scan_stack; + pp_format_stack = []; + pp_tbox_stack = []; + pp_tag_stack = []; + pp_mark_stack = []; + pp_margin = 78; + pp_min_space_left = 10; + pp_max_indent = 78 - 10; + pp_space_left = 78; + pp_current_indent = 0; + pp_is_new_line = true; + pp_left_total = 1; + pp_right_total = 1; + pp_curr_depth = 1; + pp_max_boxes = max_int; + pp_ellipsis = "."; + pp_output_function = f; + pp_flush_function = g; + pp_output_newline = h; + pp_output_spaces = i; + pp_print_tags = false; + pp_mark_tags = false; + pp_mark_open_tag = default_pp_mark_open_tag; + pp_mark_close_tag = default_pp_mark_close_tag; + pp_print_open_tag = default_pp_print_open_tag; + pp_print_close_tag = default_pp_print_close_tag; + pp_queue = pp_q + };; + +(* Default function to output spaces. *) +let blank_line = String.make 80 ' ';; +let rec display_blanks state n = + if n > 0 then + if n <= 80 then state.pp_output_function blank_line 0 n else + begin + state.pp_output_function blank_line 0 80; + display_blanks state (n - 80) + end;; + +(* Default function to output new lines. *) +let display_newline state () = state.pp_output_function "\n" 0 1;; + +let make_formatter f g = + let ff = pp_make_formatter f g ignore ignore in + ff.pp_output_newline <- display_newline ff; + ff.pp_output_spaces <- display_blanks ff; + ff;; + +let formatter_of_out_channel oc = + make_formatter (output oc) (fun () -> flush oc);; + +let formatter_of_buffer b = + make_formatter (Buffer.add_substring b) ignore;; + +let stdbuf = Buffer.create 512;; + +let str_formatter = formatter_of_buffer stdbuf;; +let std_formatter = formatter_of_out_channel stdout;; +let err_formatter = formatter_of_out_channel stderr;; + +let flush_str_formatter () = + pp_flush_queue str_formatter false; + let s = Buffer.contents stdbuf in + Buffer.reset stdbuf; + s;; + +(************************************************************** + + Basic functions on the standard formatter + + **************************************************************) + +let open_hbox = pp_open_hbox std_formatter +and open_vbox = pp_open_vbox std_formatter +and open_hvbox = pp_open_hvbox std_formatter +and open_hovbox = pp_open_hovbox std_formatter +and open_box = pp_open_box std_formatter +and close_box = pp_close_box std_formatter +and open_tag = pp_open_tag std_formatter +and close_tag = pp_close_tag std_formatter +and print_as = pp_print_as std_formatter +and print_string = pp_print_string std_formatter +and print_int = pp_print_int std_formatter +and print_float = pp_print_float std_formatter +and print_char = pp_print_char std_formatter +and print_bool = pp_print_bool std_formatter +and print_break = pp_print_break std_formatter +and print_cut = pp_print_cut std_formatter +and print_space = pp_print_space std_formatter +and force_newline = pp_force_newline std_formatter +and print_flush = pp_print_flush std_formatter +and print_newline = pp_print_newline std_formatter +and print_if_newline = pp_print_if_newline std_formatter + +and open_tbox = pp_open_tbox std_formatter +and close_tbox = pp_close_tbox std_formatter +and print_tbreak = pp_print_tbreak std_formatter + +and set_tab = pp_set_tab std_formatter +and print_tab = pp_print_tab std_formatter + +and set_margin = pp_set_margin std_formatter +and get_margin = pp_get_margin std_formatter + +and set_max_indent = pp_set_max_indent std_formatter +and get_max_indent = pp_get_max_indent std_formatter + +and set_max_boxes = pp_set_max_boxes std_formatter +and get_max_boxes = pp_get_max_boxes std_formatter +and over_max_boxes = pp_over_max_boxes std_formatter + +and set_ellipsis_text = pp_set_ellipsis_text std_formatter +and get_ellipsis_text = pp_get_ellipsis_text std_formatter + +and set_formatter_out_channel = + pp_set_formatter_out_channel std_formatter + +and set_formatter_output_functions = + pp_set_formatter_output_functions std_formatter +and get_formatter_output_functions = + pp_get_formatter_output_functions std_formatter + +and set_all_formatter_output_functions = + pp_set_all_formatter_output_functions std_formatter +and get_all_formatter_output_functions = + pp_get_all_formatter_output_functions std_formatter + +and set_formatter_tag_functions = + pp_set_formatter_tag_functions std_formatter +and get_formatter_tag_functions = + pp_get_formatter_tag_functions std_formatter +and set_print_tags = + pp_set_print_tags std_formatter +and get_print_tags = + pp_get_print_tags std_formatter +and set_mark_tags = + pp_set_mark_tags std_formatter +and get_mark_tags = + pp_get_mark_tags std_formatter +and set_tags = + pp_set_tags std_formatter +;; + + +(************************************************************** + + Printf implementation. + + **************************************************************) + +(* Error messages when processing formats. *) + +(* Trailer: giving up at character number ... *) +let giving_up mess fmt i = + "fprintf: " ^ mess ^ " ``" ^ fmt ^ "'', \ + giving up at character number " ^ string_of_int i ^ + (if i < String.length fmt + then " (" ^ String.make 1 fmt.[i] ^ ")." + else String.make 1 '.');; + +(* When an invalid format deserves a special error explanation. *) +let format_invalid_arg mess fmt i = invalid_arg (giving_up mess fmt i);; + +(* Standard invalid format. *) +let invalid_format fmt i = format_invalid_arg "bad format" fmt i;; + +(* Cannot find a valid integer into that format. *) +let invalid_integer fmt i = + invalid_arg (giving_up "bad integer specification" fmt i);; + +(* Finding an integer out of a sub-string of the format. *) +let format_int_of_string fmt i s = + let sz = + try int_of_string s with + | Failure s -> invalid_integer fmt i in + size_of_int sz;; + +(* Getting strings out of buffers. *) +let get_buffer_out b = + let s = Buffer.contents b in + Buffer.reset b; + s;; + +(* [ppf] is supposed to be a pretty-printer that outputs in buffer [b]: + to extract contents of [ppf] as a string we flush [ppf] and get the string + out of [b]. *) +let string_out b ppf = + pp_flush_queue ppf false; + get_buffer_out b;; + +(* Applies [printer] to a formatter that outputs on a fresh buffer, + then returns the resulting material. *) +let exstring printer arg = + let b = Buffer.create 512 in + let ppf = formatter_of_buffer b in + printer ppf arg; + string_out b ppf;; + +(* To turn out a character accumulator into the proper string result. *) +let implode_rev s0 = function + | [] -> s0 + | l -> String.concat "" (List.rev (s0 :: l));; + +external format_to_string : ('a, 'b, 'c, 'd) format4 -> string = "%identity";; + +(* [fprintf_out] is the printf-like function generator: given the + - [str] flag that tells if we are printing into a string, + - the [out] function that has to be called at the end of formatting, + it generates a [fprintf] function that takes as arguments a [ppf] + formatter and a printing format to print the rest of arguments + according to the format. + Regular [fprintf]-like functions of this module are obtained via partial + applications of [fprintf_out]. *) +let mkprintf str get_out = + let rec kprintf k fmt = + let fmt = format_to_string fmt in + let len = String.length fmt in + + let kpr fmt v = + let ppf = get_out fmt in + let print_as = ref None in + let pp_print_as_char c = + match !print_as with + | None -> pp_print_char ppf c + | Some size -> + pp_print_as_size ppf size (String.make 1 c); + print_as := None + and pp_print_as_string s = + match !print_as with + | None -> pp_print_string ppf s + | Some size -> + pp_print_as_size ppf size s; + print_as := None in + + let rec doprn n i = + if i >= len then Obj.magic (k ppf) else + match fmt.[i] with + | '%' -> + Printf.scan_format fmt v n i cont_s cont_a cont_t cont_f cont_m + | '@' -> + let i = succ i in + if i >= len then invalid_format fmt i else + begin match fmt.[i] with + | '[' -> + do_pp_open_box ppf n (succ i) + | ']' -> + pp_close_box ppf (); + doprn n (succ i) + | '{' -> + do_pp_open_tag ppf n (succ i) + | '}' -> + pp_close_tag ppf (); + doprn n (succ i) + | ' ' -> + pp_print_space ppf (); + doprn n (succ i) + | ',' -> + pp_print_cut ppf (); + doprn n (succ i) + | '?' -> + pp_print_flush ppf (); + doprn n (succ i) + | '.' -> + pp_print_newline ppf (); + doprn n (succ i) + | '\n' -> + pp_force_newline ppf (); + doprn n (succ i) + | ';' -> + do_pp_break ppf n (succ i) + | '<' -> + let got_size size n i = + print_as := Some size; + doprn n (skip_gt i) in + get_int n (succ i) got_size + | '@' as c -> + pp_print_as_char c; + doprn n (succ i) + | c -> invalid_format fmt i + end + | c -> + pp_print_as_char c; + doprn n (succ i) + + and cont_s n s i = + pp_print_as_string s; doprn n i + and cont_a n printer arg i = + if str then + pp_print_as_string ((Obj.magic printer : unit -> _ -> string) () arg) + else + printer ppf arg; + doprn n i + and cont_t n printer i = + if str then + pp_print_as_string ((Obj.magic printer : unit -> string) ()) + else + printer ppf; + doprn n i + and cont_f n i = + pp_print_flush ppf (); doprn n i + + and cont_m n sfmt i = + kprintf (Obj.magic (fun _ -> doprn n i)) sfmt + + and get_int n i c = + if i >= len then invalid_integer fmt i else + match fmt.[i] with + | ' ' -> get_int n (succ i) c + | '%' -> + let cont_s n s i = c (format_int_of_string fmt i s) n i + and cont_a n printer arg i = invalid_integer fmt i + and cont_t n printer i = invalid_integer fmt i + and cont_f n i = invalid_integer fmt i + and cont_m n sfmt i = invalid_integer fmt i in + Printf.scan_format fmt v n i cont_s cont_a cont_t cont_f cont_m + | _ -> + let rec get j = + if j >= len then invalid_integer fmt j else + match fmt.[j] with + | '0' .. '9' | '-' -> get (succ j) + | _ -> + let size = + if j = i then size_of_int 0 else + format_int_of_string fmt j (String.sub fmt i (j - i)) in + c size n j in + get i + + and skip_gt i = + if i >= len then invalid_format fmt i else + match fmt.[i] with + | ' ' -> skip_gt (succ i) + | '>' -> succ i + | _ -> invalid_format fmt i + + and get_box_kind i = + if i >= len then Pp_box, i else + match fmt.[i] with + | 'h' -> + let i = succ i in + if i >= len then Pp_hbox, i else + begin match fmt.[i] with + | 'o' -> + let i = succ i in + if i >= len then format_invalid_arg "bad box format" fmt i else + begin match fmt.[i] with + | 'v' -> Pp_hovbox, succ i + | c -> + format_invalid_arg + ("bad box name ho" ^ String.make 1 c) fmt i end + | 'v' -> Pp_hvbox, succ i + | c -> Pp_hbox, i + end + | 'b' -> Pp_box, succ i + | 'v' -> Pp_vbox, succ i + | _ -> Pp_box, i + + and get_tag_name n i c = + let rec get accu n i j = + if j >= len + then c (implode_rev (String.sub fmt i (j - i)) accu) n j else + match fmt.[j] with + | '>' -> c (implode_rev (String.sub fmt i (j - i)) accu) n j + | '%' -> + let s0 = String.sub fmt i (j - i) in + let cont_s n s i = get (s :: s0 :: accu) n i i + and cont_a n printer arg i = + let s = + if str + then (Obj.magic printer : unit -> _ -> string) () arg + else exstring printer arg in + get (s :: s0 :: accu) n i i + and cont_t n printer i = + let s = + if str + then (Obj.magic printer : unit -> string) () + else exstring (fun ppf () -> printer ppf) () in + get (s :: s0 :: accu) n i i + and cont_f n i = + format_invalid_arg "bad tag name specification" fmt i + and cont_m n sfmt i = + format_invalid_arg "bad tag name specification" fmt i in + Printf.scan_format fmt v n j cont_s cont_a cont_t cont_f cont_m + | c -> get accu n i (succ j) in + get [] n i i + + and do_pp_break ppf n i = + if i >= len then begin pp_print_space ppf (); doprn n i end else + match fmt.[i] with + | '<' -> + let rec got_nspaces nspaces n i = + get_int n i (got_offset nspaces) + and got_offset nspaces offset n i = + pp_print_break ppf (int_of_size nspaces) (int_of_size offset); + doprn n (skip_gt i) in + get_int n (succ i) got_nspaces + | c -> pp_print_space ppf (); doprn n i + + and do_pp_open_box ppf n i = + if i >= len then begin pp_open_box_gen ppf 0 Pp_box; doprn n i end else + match fmt.[i] with + | '<' -> + let kind, i = get_box_kind (succ i) in + let got_size size n i = + pp_open_box_gen ppf (int_of_size size) kind; + doprn n (skip_gt i) in + get_int n i got_size + | c -> pp_open_box_gen ppf 0 Pp_box; doprn n i + + and do_pp_open_tag ppf n i = + if i >= len then begin pp_open_tag ppf ""; doprn n i end else + match fmt.[i] with + | '<' -> + let got_name tag_name n i = + pp_open_tag ppf tag_name; + doprn n (skip_gt i) in + get_tag_name n (succ i) got_name + | c -> pp_open_tag ppf ""; doprn n i in + + doprn (Printf.index_of_int 0) 0 in + + Printf.kapr kpr fmt in + + kprintf;; + +(************************************************************** + + Defining [fprintf] and various flavors of [fprintf]. + + **************************************************************) + +let kfprintf k ppf = mkprintf false (fun _ -> ppf) k;; + +let fprintf ppf = kfprintf ignore ppf;; +let printf fmt = fprintf std_formatter fmt;; +let eprintf fmt = fprintf err_formatter fmt;; + +let kbprintf k b = + mkprintf false (fun _ -> formatter_of_buffer b) k;; + +let bprintf b = kbprintf ignore b;; + +let ksprintf k = + let b = Buffer.create 512 in + let k ppf = k (string_out b ppf) in + mkprintf true (fun _ -> formatter_of_buffer b) k;; + +let kprintf = ksprintf;; + +let sprintf fmt = ksprintf (fun s -> s) fmt;; + +at_exit print_flush;; diff --git a/tests/examplefiles/functional.rst b/tests/examplefiles/functional.rst new file mode 100644 index 0000000..bfe67d1 --- /dev/null +++ b/tests/examplefiles/functional.rst @@ -0,0 +1,1472 @@ +Functional Programming HOWTO +================================ + +**Version 0.30** + +(This is a first draft. Please send comments/error +reports/suggestions to amk@amk.ca. This URL is probably not going to +be the final location of the document, so be careful about linking to +it -- you may want to add a disclaimer.) + +In this document, we'll take a tour of Python's features suitable for +implementing programs in a functional style. After an introduction to +the concepts of functional programming, we'll look at language +features such as iterators and generators and relevant library modules +such as ``itertools`` and ``functools``. + + +.. contents:: + +Introduction +---------------------- + +This section explains the basic concept of functional programming; if +you're just interested in learning about Python language features, +skip to the next section. + +Programming languages support decomposing problems in several different +ways: + +* Most programming languages are **procedural**: + programs are lists of instructions that tell the computer what to + do with the program's input. + C, Pascal, and even Unix shells are procedural languages. + +* In **declarative** languages, you write a specification that describes + the problem to be solved, and the language implementation figures out + how to perform the computation efficiently. SQL is the declarative + language you're most likely to be familiar with; a SQL query describes + the data set you want to retrieve, and the SQL engine decides whether to + scan tables or use indexes, which subclauses should be performed first, + etc. + +* **Object-oriented** programs manipulate collections of objects. + Objects have internal state and support methods that query or modify + this internal state in some way. Smalltalk and Java are + object-oriented languages. C++ and Python are languages that + support object-oriented programming, but don't force the use + of object-oriented features. + +* **Functional** programming decomposes a problem into a set of functions. + Ideally, functions only take inputs and produce outputs, and don't have any + internal state that affects the output produced for a given input. + Well-known functional languages include the ML family (Standard ML, + OCaml, and other variants) and Haskell. + +The designers of some computer languages have chosen one approach to +programming that's emphasized. This often makes it difficult to +write programs that use a different approach. Other languages are +multi-paradigm languages that support several different approaches. Lisp, +C++, and Python are multi-paradigm; you can write programs or +libraries that are largely procedural, object-oriented, or functional +in all of these languages. In a large program, different sections +might be written using different approaches; the GUI might be object-oriented +while the processing logic is procedural or functional, for example. + +In a functional program, input flows through a set of functions. Each +function operates on its input and produces some output. Functional +style frowns upon functions with side effects that modify internal +state or make other changes that aren't visible in the function's +return value. Functions that have no side effects at all are +called **purely functional**. +Avoiding side effects means not using data structures +that get updated as a program runs; every function's output +must only depend on its input. + +Some languages are very strict about purity and don't even have +assignment statements such as ``a=3`` or ``c = a + b``, but it's +difficult to avoid all side effects. Printing to the screen or +writing to a disk file are side effects, for example. For example, in +Python a ``print`` statement or a ``time.sleep(1)`` both return no +useful value; they're only called for their side effects of sending +some text to the screen or pausing execution for a second. + +Python programs written in functional style usually won't go to the +extreme of avoiding all I/O or all assignments; instead, they'll +provide a functional-appearing interface but will use non-functional +features internally. For example, the implementation of a function +will still use assignments to local variables, but won't modify global +variables or have other side effects. + +Functional programming can be considered the opposite of +object-oriented programming. Objects are little capsules containing +some internal state along with a collection of method calls that let +you modify this state, and programs consist of making the right set of +state changes. Functional programming wants to avoid state changes as +much as possible and works with data flowing between functions. In +Python you might combine the two approaches by writing functions that +take and return instances representing objects in your application +(e-mail messages, transactions, etc.). + +Functional design may seem like an odd constraint to work under. Why +should you avoid objects and side effects? There are theoretical and +practical advantages to the functional style: + +* Formal provability. +* Modularity. +* Composability. +* Ease of debugging and testing. + +Formal provability +'''''''''''''''''''''' + +A theoretical benefit is that it's easier to construct a mathematical proof +that a functional program is correct. + +For a long time researchers have been interested in finding ways to +mathematically prove programs correct. This is different from testing +a program on numerous inputs and concluding that its output is usually +correct, or reading a program's source code and concluding that the +code looks right; the goal is instead a rigorous proof that a program +produces the right result for all possible inputs. + +The technique used to prove programs correct is to write down +**invariants**, properties of the input data and of the program's +variables that are always true. For each line of code, you then show +that if invariants X and Y are true **before** the line is executed, +the slightly different invariants X' and Y' are true **after** +the line is executed. This continues until you reach the end of the +program, at which point the invariants should match the desired +conditions on the program's output. + +Functional programming's avoidance of assignments arose because +assignments are difficult to handle with this technique; +assignments can break invariants that were true before the assignment +without producing any new invariants that can be propagated onward. + +Unfortunately, proving programs correct is largely impractical and not +relevant to Python software. Even trivial programs require proofs that +are several pages long; the proof of correctness for a moderately +complicated program would be enormous, and few or none of the programs +you use daily (the Python interpreter, your XML parser, your web +browser) could be proven correct. Even if you wrote down or generated +a proof, there would then be the question of verifying the proof; +maybe there's an error in it, and you wrongly believe you've proved +the program correct. + +Modularity +'''''''''''''''''''''' + +A more practical benefit of functional programming is that it forces +you to break apart your problem into small pieces. Programs are more +modular as a result. It's easier to specify and write a small +function that does one thing than a large function that performs a +complicated transformation. Small functions are also easier to read +and to check for errors. + + +Ease of debugging and testing +'''''''''''''''''''''''''''''''''' + +Testing and debugging a functional-style program is easier. + +Debugging is simplified because functions are generally small and +clearly specified. When a program doesn't work, each function is an +interface point where you can check that the data are correct. You +can look at the intermediate inputs and outputs to quickly isolate the +function that's responsible for a bug. + +Testing is easier because each function is a potential subject for a +unit test. Functions don't depend on system state that needs to be +replicated before running a test; instead you only have to synthesize +the right input and then check that the output matches expectations. + + + +Composability +'''''''''''''''''''''' + +As you work on a functional-style program, you'll write a number of +functions with varying inputs and outputs. Some of these functions +will be unavoidably specialized to a particular application, but +others will be useful in a wide variety of programs. For example, a +function that takes a directory path and returns all the XML files in +the directory, or a function that takes a filename and returns its +contents, can be applied to many different situations. + +Over time you'll form a personal library of utilities. Often you'll +assemble new programs by arranging existing functions in a new +configuration and writing a few functions specialized for the current +task. + + + +Iterators +----------------------- + +I'll start by looking at a Python language feature that's an important +foundation for writing functional-style programs: iterators. + +An iterator is an object representing a stream of data; this object +returns the data one element at a time. A Python iterator must +support a method called ``next()`` that takes no arguments and always +returns the next element of the stream. If there are no more elements +in the stream, ``next()`` must raise the ``StopIteration`` exception. +Iterators don't have to be finite, though; it's perfectly reasonable +to write an iterator that produces an infinite stream of data. + +The built-in ``iter()`` function takes an arbitrary object and tries +to return an iterator that will return the object's contents or +elements, raising ``TypeError`` if the object doesn't support +iteration. Several of Python's built-in data types support iteration, +the most common being lists and dictionaries. An object is called +an **iterable** object if you can get an iterator for it. + +You can experiment with the iteration interface manually:: + + >>> L = [1,2,3] + >>> it = iter(L) + >>> print it + + >>> it.next() + 1 + >>> it.next() + 2 + >>> it.next() + 3 + >>> it.next() + Traceback (most recent call last): + File "", line 1, in ? + StopIteration + >>> + +Python expects iterable objects in several different contexts, the +most important being the ``for`` statement. In the statement ``for X in Y``, +Y must be an iterator or some object for which ``iter()`` can create +an iterator. These two statements are equivalent:: + + for i in iter(obj): + print i + + for i in obj: + print i + +Iterators can be materialized as lists or tuples by using the +``list()`` or ``tuple()`` constructor functions:: + + >>> L = [1,2,3] + >>> iterator = iter(L) + >>> t = tuple(iterator) + >>> t + (1, 2, 3) + +Sequence unpacking also supports iterators: if you know an iterator +will return N elements, you can unpack them into an N-tuple:: + + >>> L = [1,2,3] + >>> iterator = iter(L) + >>> a,b,c = iterator + >>> a,b,c + (1, 2, 3) + +Built-in functions such as ``max()`` and ``min()`` can take a single +iterator argument and will return the largest or smallest element. +The ``"in"`` and ``"not in"`` operators also support iterators: ``X in +iterator`` is true if X is found in the stream returned by the +iterator. You'll run into obvious problems if the iterator is +infinite; ``max()``, ``min()``, and ``"not in"`` will never return, and +if the element X never appears in the stream, the ``"in"`` operator +won't return either. + +Note that you can only go forward in an iterator; there's no way to +get the previous element, reset the iterator, or make a copy of it. +Iterator objects can optionally provide these additional capabilities, +but the iterator protocol only specifies the ``next()`` method. +Functions may therefore consume all of the iterator's output, and if +you need to do something different with the same stream, you'll have +to create a new iterator. + + + +Data Types That Support Iterators +''''''''''''''''''''''''''''''''''' + +We've already seen how lists and tuples support iterators. In fact, +any Python sequence type, such as strings, will automatically support +creation of an iterator. + +Calling ``iter()`` on a dictionary returns an iterator that will loop +over the dictionary's keys:: + + >>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, + ... 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12} + >>> for key in m: + ... print key, m[key] + Mar 3 + Feb 2 + Aug 8 + Sep 9 + May 5 + Jun 6 + Jul 7 + Jan 1 + Apr 4 + Nov 11 + Dec 12 + Oct 10 + +Note that the order is essentially random, because it's based on the +hash ordering of the objects in the dictionary. + +Applying ``iter()`` to a dictionary always loops over the keys, but +dictionaries have methods that return other iterators. If you want to +iterate over keys, values, or key/value pairs, you can explicitly call +the ``iterkeys()``, ``itervalues()``, or ``iteritems()`` methods to +get an appropriate iterator. + +The ``dict()`` constructor can accept an iterator that returns a +finite stream of ``(key, value)`` tuples:: + + >>> L = [('Italy', 'Rome'), ('France', 'Paris'), ('US', 'Washington DC')] + >>> dict(iter(L)) + {'Italy': 'Rome', 'US': 'Washington DC', 'France': 'Paris'} + +Files also support iteration by calling the ``readline()`` +method until there are no more lines in the file. This means you can +read each line of a file like this:: + + for line in file: + # do something for each line + ... + +Sets can take their contents from an iterable and let you iterate over +the set's elements:: + + S = set((2, 3, 5, 7, 11, 13)) + for i in S: + print i + + + +Generator expressions and list comprehensions +---------------------------------------------------- + +Two common operations on an iterator's output are 1) performing some +operation for every element, 2) selecting a subset of elements that +meet some condition. For example, given a list of strings, you might +want to strip off trailing whitespace from each line or extract all +the strings containing a given substring. + +List comprehensions and generator expressions (short form: "listcomps" +and "genexps") are a concise notation for such operations, borrowed +from the functional programming language Haskell +(http://www.haskell.org). You can strip all the whitespace from a +stream of strings with the following code:: + + line_list = [' line 1\n', 'line 2 \n', ...] + + # Generator expression -- returns iterator + stripped_iter = (line.strip() for line in line_list) + + # List comprehension -- returns list + stripped_list = [line.strip() for line in line_list] + +You can select only certain elements by adding an ``"if"`` condition:: + + stripped_list = [line.strip() for line in line_list + if line != ""] + +With a list comprehension, you get back a Python list; +``stripped_list`` is a list containing the resulting lines, not an +iterator. Generator expressions return an iterator that computes the +values as necessary, not needing to materialize all the values at +once. This means that list comprehensions aren't useful if you're +working with iterators that return an infinite stream or a very large +amount of data. Generator expressions are preferable in these +situations. + +Generator expressions are surrounded by parentheses ("()") and list +comprehensions are surrounded by square brackets ("[]"). Generator +expressions have the form:: + + ( expression for expr in sequence1 + if condition1 + for expr2 in sequence2 + if condition2 + for expr3 in sequence3 ... + if condition3 + for exprN in sequenceN + if conditionN ) + +Again, for a list comprehension only the outside brackets are +different (square brackets instead of parentheses). + +The elements of the generated output will be the successive values of +``expression``. The ``if`` clauses are all optional; if present, +``expression`` is only evaluated and added to the result when +``condition`` is true. + +Generator expressions always have to be written inside parentheses, +but the parentheses signalling a function call also count. If you +want to create an iterator that will be immediately passed to a +function you can write:: + + obj_total = sum(obj.count for obj in list_all_objects()) + +The ``for...in`` clauses contain the sequences to be iterated over. +The sequences do not have to be the same length, because they are +iterated over from left to right, **not** in parallel. For each +element in ``sequence1``, ``sequence2`` is looped over from the +beginning. ``sequence3`` is then looped over for each +resulting pair of elements from ``sequence1`` and ``sequence2``. + +To put it another way, a list comprehension or generator expression is +equivalent to the following Python code:: + + for expr1 in sequence1: + if not (condition1): + continue # Skip this element + for expr2 in sequence2: + if not (condition2): + continue # Skip this element + ... + for exprN in sequenceN: + if not (conditionN): + continue # Skip this element + + # Output the value of + # the expression. + +This means that when there are multiple ``for...in`` clauses but no +``if`` clauses, the length of the resulting output will be equal to +the product of the lengths of all the sequences. If you have two +lists of length 3, the output list is 9 elements long:: + + seq1 = 'abc' + seq2 = (1,2,3) + >>> [ (x,y) for x in seq1 for y in seq2] + [('a', 1), ('a', 2), ('a', 3), + ('b', 1), ('b', 2), ('b', 3), + ('c', 1), ('c', 2), ('c', 3)] + +To avoid introducing an ambiguity into Python's grammar, if +``expression`` is creating a tuple, it must be surrounded with +parentheses. The first list comprehension below is a syntax error, +while the second one is correct:: + + # Syntax error + [ x,y for x in seq1 for y in seq2] + # Correct + [ (x,y) for x in seq1 for y in seq2] + + +Generators +----------------------- + +Generators are a special class of functions that simplify the task of +writing iterators. Regular functions compute a value and return it, +but generators return an iterator that returns a stream of values. + +You're doubtless familiar with how regular function calls work in +Python or C. When you call a function, it gets a private namespace +where its local variables are created. When the function reaches a +``return`` statement, the local variables are destroyed and the +value is returned to the caller. A later call to the same function +creates a new private namespace and a fresh set of local +variables. But, what if the local variables weren't thrown away on +exiting a function? What if you could later resume the function where +it left off? This is what generators provide; they can be thought of +as resumable functions. + +Here's the simplest example of a generator function:: + + def generate_ints(N): + for i in range(N): + yield i + +Any function containing a ``yield`` keyword is a generator function; +this is detected by Python's bytecode compiler which compiles the +function specially as a result. + +When you call a generator function, it doesn't return a single value; +instead it returns a generator object that supports the iterator +protocol. On executing the ``yield`` expression, the generator +outputs the value of ``i``, similar to a ``return`` +statement. The big difference between ``yield`` and a +``return`` statement is that on reaching a ``yield`` the +generator's state of execution is suspended and local variables are +preserved. On the next call to the generator's ``.next()`` method, +the function will resume executing. + +Here's a sample usage of the ``generate_ints()`` generator:: + + >>> gen = generate_ints(3) + >>> gen + + >>> gen.next() + 0 + >>> gen.next() + 1 + >>> gen.next() + 2 + >>> gen.next() + Traceback (most recent call last): + File "stdin", line 1, in ? + File "stdin", line 2, in generate_ints + StopIteration + +You could equally write ``for i in generate_ints(5)``, or +``a,b,c = generate_ints(3)``. + +Inside a generator function, the ``return`` statement can only be used +without a value, and signals the end of the procession of values; +after executing a ``return`` the generator cannot return any further +values. ``return`` with a value, such as ``return 5``, is a syntax +error inside a generator function. The end of the generator's results +can also be indicated by raising ``StopIteration`` manually, or by +just letting the flow of execution fall off the bottom of the +function. + +You could achieve the effect of generators manually by writing your +own class and storing all the local variables of the generator as +instance variables. For example, returning a list of integers could +be done by setting ``self.count`` to 0, and having the +``next()`` method increment ``self.count`` and return it. +However, for a moderately complicated generator, writing a +corresponding class can be much messier. + +The test suite included with Python's library, ``test_generators.py``, +contains a number of more interesting examples. Here's one generator +that implements an in-order traversal of a tree using generators +recursively. + +:: + + # A recursive generator that generates Tree leaves in in-order. + def inorder(t): + if t: + for x in inorder(t.left): + yield x + + yield t.label + + for x in inorder(t.right): + yield x + +Two other examples in ``test_generators.py`` produce +solutions for the N-Queens problem (placing N queens on an NxN +chess board so that no queen threatens another) and the Knight's Tour +(finding a route that takes a knight to every square of an NxN chessboard +without visiting any square twice). + + + +Passing values into a generator +'''''''''''''''''''''''''''''''''''''''''''''' + +In Python 2.4 and earlier, generators only produced output. Once a +generator's code was invoked to create an iterator, there was no way to +pass any new information into the function when its execution is +resumed. You could hack together this ability by making the +generator look at a global variable or by passing in some mutable object +that callers then modify, but these approaches are messy. + +In Python 2.5 there's a simple way to pass values into a generator. +``yield`` became an expression, returning a value that can be assigned +to a variable or otherwise operated on:: + + val = (yield i) + +I recommend that you **always** put parentheses around a ``yield`` +expression when you're doing something with the returned value, as in +the above example. The parentheses aren't always necessary, but it's +easier to always add them instead of having to remember when they're +needed. + +(PEP 342 explains the exact rules, which are that a +``yield``-expression must always be parenthesized except when it +occurs at the top-level expression on the right-hand side of an +assignment. This means you can write ``val = yield i`` but have to +use parentheses when there's an operation, as in ``val = (yield i) ++ 12``.) + +Values are sent into a generator by calling its +``send(value)`` method. This method resumes the +generator's code and the ``yield`` expression returns the specified +value. If the regular ``next()`` method is called, the +``yield`` returns ``None``. + +Here's a simple counter that increments by 1 and allows changing the +value of the internal counter. + +:: + + def counter (maximum): + i = 0 + while i < maximum: + val = (yield i) + # If value provided, change counter + if val is not None: + i = val + else: + i += 1 + +And here's an example of changing the counter: + + >>> it = counter(10) + >>> print it.next() + 0 + >>> print it.next() + 1 + >>> print it.send(8) + 8 + >>> print it.next() + 9 + >>> print it.next() + Traceback (most recent call last): + File ``t.py'', line 15, in ? + print it.next() + StopIteration + +Because ``yield`` will often be returning ``None``, you +should always check for this case. Don't just use its value in +expressions unless you're sure that the ``send()`` method +will be the only method used resume your generator function. + +In addition to ``send()``, there are two other new methods on +generators: + +* ``throw(type, value=None, traceback=None)`` is used to raise an exception inside the + generator; the exception is raised by the ``yield`` expression + where the generator's execution is paused. + +* ``close()`` raises a ``GeneratorExit`` + exception inside the generator to terminate the iteration. + On receiving this + exception, the generator's code must either raise + ``GeneratorExit`` or ``StopIteration``; catching the + exception and doing anything else is illegal and will trigger + a ``RuntimeError``. ``close()`` will also be called by + Python's garbage collector when the generator is garbage-collected. + + If you need to run cleanup code when a ``GeneratorExit`` occurs, + I suggest using a ``try: ... finally:`` suite instead of + catching ``GeneratorExit``. + +The cumulative effect of these changes is to turn generators from +one-way producers of information into both producers and consumers. + +Generators also become **coroutines**, a more generalized form of +subroutines. Subroutines are entered at one point and exited at +another point (the top of the function, and a ``return`` +statement), but coroutines can be entered, exited, and resumed at +many different points (the ``yield`` statements). + + +Built-in functions +---------------------------------------------- + +Let's look in more detail at built-in functions often used with iterators. + +Two Python's built-in functions, ``map()`` and ``filter()``, are +somewhat obsolete; they duplicate the features of list comprehensions +but return actual lists instead of iterators. + +``map(f, iterA, iterB, ...)`` returns a list containing ``f(iterA[0], +iterB[0]), f(iterA[1], iterB[1]), f(iterA[2], iterB[2]), ...``. + +:: + + def upper(s): + return s.upper() + map(upper, ['sentence', 'fragment']) => + ['SENTENCE', 'FRAGMENT'] + + [upper(s) for s in ['sentence', 'fragment']] => + ['SENTENCE', 'FRAGMENT'] + +As shown above, you can achieve the same effect with a list +comprehension. The ``itertools.imap()`` function does the same thing +but can handle infinite iterators; it'll be discussed later, in the section on +the ``itertools`` module. + +``filter(predicate, iter)`` returns a list +that contains all the sequence elements that meet a certain condition, +and is similarly duplicated by list comprehensions. +A **predicate** is a function that returns the truth value of +some condition; for use with ``filter()``, the predicate must take a +single value. + +:: + + def is_even(x): + return (x % 2) == 0 + + filter(is_even, range(10)) => + [0, 2, 4, 6, 8] + +This can also be written as a list comprehension:: + + >>> [x for x in range(10) if is_even(x)] + [0, 2, 4, 6, 8] + +``filter()`` also has a counterpart in the ``itertools`` module, +``itertools.ifilter()``, that returns an iterator and +can therefore handle infinite sequences just as ``itertools.imap()`` can. + +``reduce(func, iter, [initial_value])`` doesn't have a counterpart in +the ``itertools`` module because it cumulatively performs an operation +on all the iterable's elements and therefore can't be applied to +infinite iterables. ``func`` must be a function that takes two elements +and returns a single value. ``reduce()`` takes the first two elements +A and B returned by the iterator and calculates ``func(A, B)``. It +then requests the third element, C, calculates ``func(func(A, B), +C)``, combines this result with the fourth element returned, and +continues until the iterable is exhausted. If the iterable returns no +values at all, a ``TypeError`` exception is raised. If the initial +value is supplied, it's used as a starting point and +``func(initial_value, A)`` is the first calculation. + +:: + + import operator + reduce(operator.concat, ['A', 'BB', 'C']) => + 'ABBC' + reduce(operator.concat, []) => + TypeError: reduce() of empty sequence with no initial value + reduce(operator.mul, [1,2,3], 1) => + 6 + reduce(operator.mul, [], 1) => + 1 + +If you use ``operator.add`` with ``reduce()``, you'll add up all the +elements of the iterable. This case is so common that there's a special +built-in called ``sum()`` to compute it:: + + reduce(operator.add, [1,2,3,4], 0) => + 10 + sum([1,2,3,4]) => + 10 + sum([]) => + 0 + +For many uses of ``reduce()``, though, it can be clearer to just write +the obvious ``for`` loop:: + + # Instead of: + product = reduce(operator.mul, [1,2,3], 1) + + # You can write: + product = 1 + for i in [1,2,3]: + product *= i + + +``enumerate(iter)`` counts off the elements in the iterable, returning +2-tuples containing the count and each element. + +:: + + enumerate(['subject', 'verb', 'object']) => + (0, 'subject'), (1, 'verb'), (2, 'object') + +``enumerate()`` is often used when looping through a list +and recording the indexes at which certain conditions are met:: + + f = open('data.txt', 'r') + for i, line in enumerate(f): + if line.strip() == '': + print 'Blank line at line #%i' % i + +``sorted(iterable, [cmp=None], [key=None], [reverse=False)`` +collects all the elements of the iterable into a list, sorts +the list, and returns the sorted result. The ``cmp``, ``key``, +and ``reverse`` arguments are passed through to the +constructed list's ``.sort()`` method. + +:: + + import random + # Generate 8 random numbers between [0, 10000) + rand_list = random.sample(range(10000), 8) + rand_list => + [769, 7953, 9828, 6431, 8442, 9878, 6213, 2207] + sorted(rand_list) => + [769, 2207, 6213, 6431, 7953, 8442, 9828, 9878] + sorted(rand_list, reverse=True) => + [9878, 9828, 8442, 7953, 6431, 6213, 2207, 769] + +(For a more detailed discussion of sorting, see the Sorting mini-HOWTO +in the Python wiki at http://wiki.python.org/moin/HowTo/Sorting.) + +The ``any(iter)`` and ``all(iter)`` built-ins look at +the truth values of an iterable's contents. ``any()`` returns +True if any element in the iterable is a true value, and ``all()`` +returns True if all of the elements are true values:: + + any([0,1,0]) => + True + any([0,0,0]) => + False + any([1,1,1]) => + True + all([0,1,0]) => + False + all([0,0,0]) => + False + all([1,1,1]) => + True + + +Small functions and the lambda statement +---------------------------------------------- + +When writing functional-style programs, you'll often need little +functions that act as predicates or that combine elements in some way. + +If there's a Python built-in or a module function that's suitable, you +don't need to define a new function at all:: + + stripped_lines = [line.strip() for line in lines] + existing_files = filter(os.path.exists, file_list) + +If the function you need doesn't exist, you need to write it. One way +to write small functions is to use the ``lambda`` statement. ``lambda`` +takes a number of parameters and an expression combining these parameters, +and creates a small function that returns the value of the expression:: + + lowercase = lambda x: x.lower() + + print_assign = lambda name, value: name + '=' + str(value) + + adder = lambda x, y: x+y + +An alternative is to just use the ``def`` statement and define a +function in the usual way:: + + def lowercase(x): + return x.lower() + + def print_assign(name, value): + return name + '=' + str(value) + + def adder(x,y): + return x + y + +Which alternative is preferable? That's a style question; my usual +course is to avoid using ``lambda``. + +One reason for my preference is that ``lambda`` is quite limited in +the functions it can define. The result has to be computable as a +single expression, which means you can't have multiway +``if... elif... else`` comparisons or ``try... except`` statements. +If you try to do too much in a ``lambda`` statement, you'll end up +with an overly complicated expression that's hard to read. Quick, +what's the following code doing? + +:: + + total = reduce(lambda a, b: (0, a[1] + b[1]), items)[1] + +You can figure it out, but it takes time to disentangle the expression +to figure out what's going on. Using a short nested +``def`` statements makes things a little bit better:: + + def combine (a, b): + return 0, a[1] + b[1] + + total = reduce(combine, items)[1] + +But it would be best of all if I had simply used a ``for`` loop:: + + total = 0 + for a, b in items: + total += b + +Or the ``sum()`` built-in and a generator expression:: + + total = sum(b for a,b in items) + +Many uses of ``reduce()`` are clearer when written as ``for`` loops. + +Fredrik Lundh once suggested the following set of rules for refactoring +uses of ``lambda``: + +1) Write a lambda function. +2) Write a comment explaining what the heck that lambda does. +3) Study the comment for a while, and think of a name that captures + the essence of the comment. +4) Convert the lambda to a def statement, using that name. +5) Remove the comment. + +I really like these rules, but you're free to disagree that this +lambda-free style is better. + + +The itertools module +----------------------- + +The ``itertools`` module contains a number of commonly-used iterators +as well as functions for combining several iterators. This section +will introduce the module's contents by showing small examples. + +The module's functions fall into a few broad classes: + +* Functions that create a new iterator based on an existing iterator. +* Functions for treating an iterator's elements as function arguments. +* Functions for selecting portions of an iterator's output. +* A function for grouping an iterator's output. + +Creating new iterators +'''''''''''''''''''''' + +``itertools.count(n)`` returns an infinite stream of +integers, increasing by 1 each time. You can optionally supply the +starting number, which defaults to 0:: + + itertools.count() => + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... + itertools.count(10) => + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ... + +``itertools.cycle(iter)`` saves a copy of the contents of a provided +iterable and returns a new iterator that returns its elements from +first to last. The new iterator will repeat these elements infinitely. + +:: + + itertools.cycle([1,2,3,4,5]) => + 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ... + +``itertools.repeat(elem, [n])`` returns the provided element ``n`` +times, or returns the element endlessly if ``n`` is not provided. + +:: + + itertools.repeat('abc') => + abc, abc, abc, abc, abc, abc, abc, abc, abc, abc, ... + itertools.repeat('abc', 5) => + abc, abc, abc, abc, abc + +``itertools.chain(iterA, iterB, ...)`` takes an arbitrary number of +iterables as input, and returns all the elements of the first +iterator, then all the elements of the second, and so on, until all of +the iterables have been exhausted. + +:: + + itertools.chain(['a', 'b', 'c'], (1, 2, 3)) => + a, b, c, 1, 2, 3 + +``itertools.izip(iterA, iterB, ...)`` takes one element from each iterable +and returns them in a tuple:: + + itertools.izip(['a', 'b', 'c'], (1, 2, 3)) => + ('a', 1), ('b', 2), ('c', 3) + +It's similiar to the built-in ``zip()`` function, but doesn't +construct an in-memory list and exhaust all the input iterators before +returning; instead tuples are constructed and returned only if they're +requested. (The technical term for this behaviour is +`lazy evaluation `__.) + +This iterator is intended to be used with iterables that are all of +the same length. If the iterables are of different lengths, the +resulting stream will be the same length as the shortest iterable. + +:: + + itertools.izip(['a', 'b'], (1, 2, 3)) => + ('a', 1), ('b', 2) + +You should avoid doing this, though, because an element may be taken +from the longer iterators and discarded. This means you can't go on +to use the iterators further because you risk skipping a discarded +element. + +``itertools.islice(iter, [start], stop, [step])`` returns a stream +that's a slice of the iterator. With a single ``stop`` argument, +it will return the first ``stop`` +elements. If you supply a starting index, you'll get ``stop-start`` +elements, and if you supply a value for ``step``, elements will be +skipped accordingly. Unlike Python's string and list slicing, you +can't use negative values for ``start``, ``stop``, or ``step``. + +:: + + itertools.islice(range(10), 8) => + 0, 1, 2, 3, 4, 5, 6, 7 + itertools.islice(range(10), 2, 8) => + 2, 3, 4, 5, 6, 7 + itertools.islice(range(10), 2, 8, 2) => + 2, 4, 6 + +``itertools.tee(iter, [n])`` replicates an iterator; it returns ``n`` +independent iterators that will all return the contents of the source +iterator. If you don't supply a value for ``n``, the default is 2. +Replicating iterators requires saving some of the contents of the source +iterator, so this can consume significant memory if the iterator is large +and one of the new iterators is consumed more than the others. + +:: + + itertools.tee( itertools.count() ) => + iterA, iterB + + where iterA -> + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... + + and iterB -> + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... + + +Calling functions on elements +''''''''''''''''''''''''''''' + +Two functions are used for calling other functions on the contents of an +iterable. + +``itertools.imap(f, iterA, iterB, ...)`` returns +a stream containing ``f(iterA[0], iterB[0]), f(iterA[1], iterB[1]), +f(iterA[2], iterB[2]), ...``:: + + itertools.imap(operator.add, [5, 6, 5], [1, 2, 3]) => + 6, 8, 8 + +The ``operator`` module contains a set of functions +corresponding to Python's operators. Some examples are +``operator.add(a, b)`` (adds two values), +``operator.ne(a, b)`` (same as ``a!=b``), +and +``operator.attrgetter('id')`` (returns a callable that +fetches the ``"id"`` attribute). + +``itertools.starmap(func, iter)`` assumes that the iterable will +return a stream of tuples, and calls ``f()`` using these tuples as the +arguments:: + + itertools.starmap(os.path.join, + [('/usr', 'bin', 'java'), ('/bin', 'python'), + ('/usr', 'bin', 'perl'),('/usr', 'bin', 'ruby')]) + => + /usr/bin/java, /bin/python, /usr/bin/perl, /usr/bin/ruby + + +Selecting elements +'''''''''''''''''' + +Another group of functions chooses a subset of an iterator's elements +based on a predicate. + +``itertools.ifilter(predicate, iter)`` returns all the elements for +which the predicate returns true:: + + def is_even(x): + return (x % 2) == 0 + + itertools.ifilter(is_even, itertools.count()) => + 0, 2, 4, 6, 8, 10, 12, 14, ... + +``itertools.ifilterfalse(predicate, iter)`` is the opposite, +returning all elements for which the predicate returns false:: + + itertools.ifilterfalse(is_even, itertools.count()) => + 1, 3, 5, 7, 9, 11, 13, 15, ... + +``itertools.takewhile(predicate, iter)`` returns elements for as long +as the predicate returns true. Once the predicate returns false, +the iterator will signal the end of its results. + +:: + + def less_than_10(x): + return (x < 10) + + itertools.takewhile(less_than_10, itertools.count()) => + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + + itertools.takewhile(is_even, itertools.count()) => + 0 + +``itertools.dropwhile(predicate, iter)`` discards elements while the +predicate returns true, and then returns the rest of the iterable's +results. + +:: + + itertools.dropwhile(less_than_10, itertools.count()) => + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ... + + itertools.dropwhile(is_even, itertools.count()) => + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ... + + +Grouping elements +''''''''''''''''' + +The last function I'll discuss, ``itertools.groupby(iter, +key_func=None)``, is the most complicated. ``key_func(elem)`` is a +function that can compute a key value for each element returned by the +iterable. If you don't supply a key function, the key is simply each +element itself. + +``groupby()`` collects all the consecutive elements from the +underlying iterable that have the same key value, and returns a stream +of 2-tuples containing a key value and an iterator for the elements +with that key. + +:: + + city_list = [('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL'), + ('Anchorage', 'AK'), ('Nome', 'AK'), + ('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ'), + ... + ] + + def get_state ((city, state)): + return state + + itertools.groupby(city_list, get_state) => + ('AL', iterator-1), + ('AK', iterator-2), + ('AZ', iterator-3), ... + + where + iterator-1 => + ('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL') + iterator-2 => + ('Anchorage', 'AK'), ('Nome', 'AK') + iterator-3 => + ('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ') + +``groupby()`` assumes that the underlying iterable's contents will +already be sorted based on the key. Note that the returned iterators +also use the underlying iterable, so you have to consume the results +of iterator-1 before requesting iterator-2 and its corresponding key. + + +The functools module +---------------------------------------------- + +The ``functools`` module in Python 2.5 contains some higher-order +functions. A **higher-order function** takes one or more functions as +input and returns a new function. The most useful tool in this module +is the ``partial()`` function. + +For programs written in a functional style, you'll sometimes want to +construct variants of existing functions that have some of the +parameters filled in. Consider a Python function ``f(a, b, c)``; you +may wish to create a new function ``g(b, c)`` that's equivalent to +``f(1, b, c)``; you're filling in a value for one of ``f()``'s parameters. +This is called "partial function application". + +The constructor for ``partial`` takes the arguments ``(function, arg1, +arg2, ... kwarg1=value1, kwarg2=value2)``. The resulting object is +callable, so you can just call it to invoke ``function`` with the +filled-in arguments. + +Here's a small but realistic example:: + + import functools + + def log (message, subsystem): + "Write the contents of 'message' to the specified subsystem." + print '%s: %s' % (subsystem, message) + ... + + server_log = functools.partial(log, subsystem='server') + server_log('Unable to open socket') + + +The operator module +------------------- + +The ``operator`` module was mentioned earlier. It contains a set of +functions corresponding to Python's operators. These functions +are often useful in functional-style code because they save you +from writing trivial functions that perform a single operation. + +Some of the functions in this module are: + +* Math operations: ``add()``, ``sub()``, ``mul()``, ``div()``, ``floordiv()``, + ``abs()``, ... +* Logical operations: ``not_()``, ``truth()``. +* Bitwise operations: ``and_()``, ``or_()``, ``invert()``. +* Comparisons: ``eq()``, ``ne()``, ``lt()``, ``le()``, ``gt()``, and ``ge()``. +* Object identity: ``is_()``, ``is_not()``. + +Consult `the operator module's documentation `__ for a complete +list. + + + +The functional module +--------------------- + +Collin Winter's `functional module `__ +provides a number of more +advanced tools for functional programming. It also reimplements +several Python built-ins, trying to make them more intuitive to those +used to functional programming in other languages. + +This section contains an introduction to some of the most important +functions in ``functional``; full documentation can be found at `the +project's website `__. + +``compose(outer, inner, unpack=False)`` + +The ``compose()`` function implements function composition. +In other words, it returns a wrapper around the ``outer`` and ``inner`` callables, such +that the return value from ``inner`` is fed directly to ``outer``. That is, + +:: + + >>> def add(a, b): + ... return a + b + ... + >>> def double(a): + ... return 2 * a + ... + >>> compose(double, add)(5, 6) + 22 + +is equivalent to + +:: + + >>> double(add(5, 6)) + 22 + +The ``unpack`` keyword is provided to work around the fact that Python functions are not always +`fully curried `__. +By default, it is expected that the ``inner`` function will return a single object and that the ``outer`` +function will take a single argument. Setting the ``unpack`` argument causes ``compose`` to expect a +tuple from ``inner`` which will be expanded before being passed to ``outer``. Put simply, + +:: + + compose(f, g)(5, 6) + +is equivalent to:: + + f(g(5, 6)) + +while + +:: + + compose(f, g, unpack=True)(5, 6) + +is equivalent to:: + + f(*g(5, 6)) + +Even though ``compose()`` only accepts two functions, it's trivial to +build up a version that will compose any number of functions. We'll +use ``reduce()``, ``compose()`` and ``partial()`` (the last of which +is provided by both ``functional`` and ``functools``). + +:: + + from functional import compose, partial + + multi_compose = partial(reduce, compose) + + +We can also use ``map()``, ``compose()`` and ``partial()`` to craft a +version of ``"".join(...)`` that converts its arguments to string:: + + from functional import compose, partial + + join = compose("".join, partial(map, str)) + + +``flip(func)`` + +``flip()`` wraps the callable in ``func`` and +causes it to receive its non-keyword arguments in reverse order. + +:: + + >>> def triple(a, b, c): + ... return (a, b, c) + ... + >>> triple(5, 6, 7) + (5, 6, 7) + >>> + >>> flipped_triple = flip(triple) + >>> flipped_triple(5, 6, 7) + (7, 6, 5) + +``foldl(func, start, iterable)`` + +``foldl()`` takes a binary function, a starting value (usually some kind of 'zero'), and an iterable. +The function is applied to the starting value and the first element of the list, then the result of +that and the second element of the list, then the result of that and the third element of the list, +and so on. + +This means that a call such as:: + + foldl(f, 0, [1, 2, 3]) + +is equivalent to:: + + f(f(f(0, 1), 2), 3) + + +``foldl()`` is roughly equivalent to the following recursive function:: + + def foldl(func, start, seq): + if len(seq) == 0: + return start + + return foldl(func, func(start, seq[0]), seq[1:]) + +Speaking of equivalence, the above ``foldl`` call can be expressed in terms of the built-in ``reduce`` like +so:: + + reduce(f, [1, 2, 3], 0) + + +We can use ``foldl()``, ``operator.concat()`` and ``partial()`` to +write a cleaner, more aesthetically-pleasing version of Python's +``"".join(...)`` idiom:: + + from functional import foldl, partial + from operator import concat + + join = partial(foldl, concat, "") + + +Revision History and Acknowledgements +------------------------------------------------ + +The author would like to thank the following people for offering +suggestions, corrections and assistance with various drafts of this +article: Ian Bicking, Nick Coghlan, Nick Efford, Raymond Hettinger, +Jim Jewett, Mike Krell, Leandro Lameiro, Jussi Salmela, +Collin Winter, Blake Winton. + +Version 0.1: posted June 30 2006. + +Version 0.11: posted July 1 2006. Typo fixes. + +Version 0.2: posted July 10 2006. Merged genexp and listcomp +sections into one. Typo fixes. + +Version 0.21: Added more references suggested on the tutor mailing list. + +Version 0.30: Adds a section on the ``functional`` module written by +Collin Winter; adds short section on the operator module; a few other +edits. + + +References +-------------------- + +General +''''''''''''''' + +**Structure and Interpretation of Computer Programs**, by +Harold Abelson and Gerald Jay Sussman with Julie Sussman. +Full text at http://mitpress.mit.edu/sicp/. +In this classic textbook of computer science, chapters 2 and 3 discuss the +use of sequences and streams to organize the data flow inside a +program. The book uses Scheme for its examples, but many of the +design approaches described in these chapters are applicable to +functional-style Python code. + +http://www.defmacro.org/ramblings/fp.html: A general +introduction to functional programming that uses Java examples +and has a lengthy historical introduction. + +http://en.wikipedia.org/wiki/Functional_programming: +General Wikipedia entry describing functional programming. + +http://en.wikipedia.org/wiki/Coroutine: +Entry for coroutines. + +http://en.wikipedia.org/wiki/Currying: +Entry for the concept of currying. + +Python-specific +''''''''''''''''''''''''''' + +http://gnosis.cx/TPiP/: +The first chapter of David Mertz's book :title-reference:`Text Processing in Python` +discusses functional programming for text processing, in the section titled +"Utilizing Higher-Order Functions in Text Processing". + +Mertz also wrote a 3-part series of articles on functional programming +for IBM's DeveloperWorks site; see +`part 1 `__, +`part 2 `__, and +`part 3 `__, + + +Python documentation +''''''''''''''''''''''''''' + +http://docs.python.org/lib/module-itertools.html: +Documentation for the ``itertools`` module. + +http://docs.python.org/lib/module-operator.html: +Documentation for the ``operator`` module. + +http://www.python.org/dev/peps/pep-0289/: +PEP 289: "Generator Expressions" + +http://www.python.org/dev/peps/pep-0342/ +PEP 342: "Coroutines via Enhanced Generators" describes the new generator +features in Python 2.5. + +.. comment + + Topics to place + ----------------------------- + + XXX os.walk() + + XXX Need a large example. + + But will an example add much? I'll post a first draft and see + what the comments say. + +.. comment + + Original outline: + Introduction + Idea of FP + Programs built out of functions + Functions are strictly input-output, no internal state + Opposed to OO programming, where objects have state + + Why FP? + Formal provability + Assignment is difficult to reason about + Not very relevant to Python + Modularity + Small functions that do one thing + Debuggability: + Easy to test due to lack of state + Easy to verify output from intermediate steps + Composability + You assemble a toolbox of functions that can be mixed + + Tackling a problem + Need a significant example + + Iterators + Generators + The itertools module + List comprehensions + Small functions and the lambda statement + Built-in functions + map + filter + reduce + +.. comment + + Handy little function for printing part of an iterator -- used + while writing this document. + + import itertools + def print_iter(it): + slice = itertools.islice(it, 10) + for elem in slice[:-1]: + sys.stdout.write(str(elem)) + sys.stdout.write(', ') + print elem[-1] + + diff --git a/tests/examplefiles/genclass.clj b/tests/examplefiles/genclass.clj new file mode 100644 index 0000000..c63da8f --- /dev/null +++ b/tests/examplefiles/genclass.clj @@ -0,0 +1,510 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(in-ns 'clojure) + +(import '(java.lang.reflect Modifier Constructor) + '(clojure.asm ClassWriter ClassVisitor Opcodes Type) + '(clojure.asm.commons Method GeneratorAdapter) + '(clojure.lang IPersistentMap)) + +;(defn method-sig [#^java.lang.reflect.Method meth] +; [(. meth (getName)) (seq (. meth (getParameterTypes)))]) + +(defn- non-private-methods [#^Class c] + (loop [mm {} + considered #{} + c c] + (if c + (let [[mm considered] + (loop [mm mm + considered considered + meths (concat + (seq (. c (getDeclaredMethods))) + (seq (. c (getMethods))))] + (if meths + (let [#^Method meth (first meths) + mods (. meth (getModifiers)) + mk (method-sig meth)] + (if (or (considered mk) + (. Modifier (isPrivate mods)) + (. Modifier (isStatic mods)) + (. Modifier (isFinal mods))) + (recur mm (conj considered mk) (rest meths)) + (recur (assoc mm mk meth) (conj considered mk) (rest meths)))) + [mm considered]))] + (recur mm considered (. c (getSuperclass)))) + mm))) + +(defn- ctor-sigs [super] + (for [#^Constructor ctor (. super (getDeclaredConstructors)) + :when (not (. Modifier (isPrivate (. ctor (getModifiers)))))] + (apply vector (. ctor (getParameterTypes))))) + +(defn- escape-class-name [c] + (.. (.getSimpleName c) + (replace "[]" "<>"))) + +(defn- overload-name [mname pclasses] + (if (seq pclasses) + (apply str mname (interleave (repeat \-) + (map escape-class-name pclasses))) + (str mname "-void"))) + +;(distinct (map first(keys (mapcat non-private-methods [Object IPersistentMap])))) + +(defn gen-class + "Generates compiled bytecode for a class with the given + package-qualified cname (which, as all names in these parameters, can + be a string or symbol). The gen-class construct contains no + implementation, as the implementation will be dynamically sought by + the generated class in functions in a corresponding Clojure + namespace. Given a generated class org.mydomain.MyClass, methods + will be implemented that look for same-named functions in a Clojure + namespace called org.domain.MyClass. The init and main + functions (see below) will be found similarly. The static + initializer for the generated class will attempt to load the Clojure + support code for the class as a resource from the claspath, e.g. in + the example case, org/mydomain/MyClass.clj + + Returns a map containing :name and :bytecode. Most uses will be + satisfied by the higher-level gen-and-load-class and + gen-and-store-class functions, which generate and immediately load, + or generate and store to disk, respectively. + + Options should be a set of key/value pairs, all of which are optional: + + :extends aclass + + Specifies the superclass, the non-private methods of which will be + overridden by the class. If not provided, defaults to Object. + + :implements [interface ...] + + One or more interfaces, the methods of which will be implemented by the class. + + :init name + + If supplied, names a function that will be called with the arguments + to the constructor. Must return [[superclass-constructor-args] state] + If not supplied, the constructor args are passed directly to + the superclass constructor and the state will be nil + + :constructors {[param-types] [super-param-types], ...} + + By default, constructors are created for the generated class which + match the signature(s) of the constructors for the superclass. This + parameter may be used to explicitly specify constructors, each entry + providing a mapping from a constructor signature to a superclass + constructor signature. When you supply this, you must supply an :init + specifier. + + :methods [[name [param-types] return-type], ...] + + The generated class automatically defines all of the non-private + methods of its superclasses/interfaces. This parameter can be used + to specify the signatures of additional methods of the generated + class. Do not repeat superclass/interface signatures here. + + :main boolean + + If supplied and true, a static public main function will be + generated. It will pass each string of the String[] argument as a + separate argument to a function called 'main. + + :factory name + + If supplied, a (set of) public static factory function(s) will be + created with the given name, and the same signature(s) as the + constructor(s). + + :state name + + If supplied, a public final instance field with the given name will be + created. You must supply an :init function in order to provide a + value for the state. Note that, though final, the state can be a ref + or agent, supporting the creation of Java objects with transactional + or asynchronous mutation semantics. + + :exposes {protected-field-name {:get name :set name}, ...} + + Since the implementations of the methods of the generated class + occur in Clojure functions, they have no access to the inherited + protected fields of the superclass. This parameter can be used to + generate public getter/setter methods exposing the protected field(s) + for use in the implementation." + + [cname & options] + (let [name (str cname) + {:keys [extends implements constructors methods main factory state init exposes]} (apply hash-map options) + super (or extends Object) + interfaces implements + supers (cons super (seq interfaces)) + ctor-sig-map (or constructors (zipmap (ctor-sigs super) (ctor-sigs super))) + cv (new ClassWriter (. ClassWriter COMPUTE_MAXS)) + cname (. name (replace "." "/")) + ctype (. Type (getObjectType cname)) + iname (fn [c] (.. Type (getType c) (getInternalName))) + totype (fn [c] (. Type (getType c))) + to-types (fn [cs] (if (pos? (count cs)) + (into-array (map totype cs)) + (make-array Type 0))) + obj-type (totype Object) + arg-types (fn [n] (if (pos? n) + (into-array (replicate n obj-type)) + (make-array Type 0))) + super-type (totype super) + init-name (str init) + factory-name (str factory) + state-name (str state) + main-name "main" + var-name (fn [s] (str s "__var")) + rt-type (totype clojure.lang.RT) + var-type (totype clojure.lang.Var) + ifn-type (totype clojure.lang.IFn) + iseq-type (totype clojure.lang.ISeq) + ex-type (totype java.lang.UnsupportedOperationException) + all-sigs (distinct (concat (map #(let[[m p] (key %)] {m [p]}) (mapcat non-private-methods supers)) + (map (fn [[m p]] {(str m) [p]}) methods))) + sigs-by-name (apply merge-with concat {} all-sigs) + overloads (into {} (filter (fn [[m s]] (rest s)) sigs-by-name)) + var-fields (concat (and init [init-name]) + (and main [main-name]) + (distinct (concat (keys sigs-by-name) + (mapcat (fn [[m s]] (map #(overload-name m %) s)) overloads) + (mapcat (comp (partial map str) vals val) exposes)))) + emit-get-var (fn [gen v] + (let [false-label (. gen newLabel) + end-label (. gen newLabel)] + (. gen getStatic ctype (var-name v) var-type) + (. gen dup) + (. gen invokeVirtual var-type (. Method (getMethod "boolean isBound()"))) + (. gen ifZCmp (. GeneratorAdapter EQ) false-label) + (. gen invokeVirtual var-type (. Method (getMethod "Object get()"))) + (. gen goTo end-label) + (. gen mark false-label) + (. gen pop) + (. gen visitInsn (. Opcodes ACONST_NULL)) + (. gen mark end-label))) + emit-forwarding-method + (fn [mname pclasses rclass else-gen] + (let [ptypes (to-types pclasses) + rtype (totype rclass) + m (new Method mname rtype ptypes) + is-overload (overloads mname) + gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv) + found-label (. gen (newLabel)) + else-label (. gen (newLabel)) + end-label (. gen (newLabel))] + (. gen (visitCode)) + (when is-overload + (emit-get-var gen (overload-name mname pclasses)) + (. gen (dup)) + (. gen (ifNonNull found-label)) + (. gen (pop))) + (emit-get-var gen mname) + (. gen (dup)) + (. gen (ifNull else-label)) + (when is-overload + (. gen (mark found-label))) + ;if found + (. gen (loadThis)) + ;box args + (dotimes i (count ptypes) + (. gen (loadArg i)) + (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i)))) + ;call fn + (. gen (invokeInterface ifn-type (new Method "invoke" obj-type + (into-array (cons obj-type + (replicate (count ptypes) obj-type)))))) + ;unbox return + (. gen (unbox rtype)) + (when (= (. rtype (getSort)) (. Type VOID)) + (. gen (pop))) + (. gen (goTo end-label)) + + ;else call supplied alternative generator + (. gen (mark else-label)) + (. gen (pop)) + + (else-gen gen m) + + (. gen (mark end-label)) + (. gen (returnValue)) + (. gen (endMethod)))) + ] + ;start class definition + (. cv (visit (. Opcodes V1_5) (. Opcodes ACC_PUBLIC) + cname nil (iname super) + (when interfaces + (into-array (map iname interfaces))))) + + ;static fields for vars + (doseq v var-fields + (. cv (visitField (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_FINAL) (. Opcodes ACC_STATIC)) + (var-name v) + (. var-type getDescriptor) + nil nil))) + + ;instance field for state + (when state + (. cv (visitField (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_FINAL)) + state-name + (. obj-type getDescriptor) + nil nil))) + + ;static init to set up var fields and load clj + (let [gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC)) + (. Method getMethod "void ()") + nil nil cv)] + (. gen (visitCode)) + (doseq v var-fields + (. gen push name) + (. gen push v) + (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.Var var(String,String)")))) + (. gen putStatic ctype (var-name v) var-type)) + + (. gen push ctype) + (. gen push (str (. name replace \. (. java.io.File separatorChar)) ".clj")) + (. gen (invokeStatic rt-type (. Method (getMethod "void loadResourceScript(Class,String)")))) + + (. gen (returnValue)) + (. gen (endMethod))) + + ;ctors + (doseq [pclasses super-pclasses] ctor-sig-map + (let [ptypes (to-types pclasses) + super-ptypes (to-types super-pclasses) + m (new Method "" (. Type VOID_TYPE) ptypes) + super-m (new Method "" (. Type VOID_TYPE) super-ptypes) + gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv) + no-init-label (. gen newLabel) + end-label (. gen newLabel) + nth-method (. Method (getMethod "Object nth(Object,int)")) + local (. gen newLocal obj-type)] + (. gen (visitCode)) + + (if init + (do + (emit-get-var gen init-name) + (. gen dup) + (. gen ifNull no-init-label) + ;box init args + (dotimes i (count pclasses) + (. gen (loadArg i)) + (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i)))) + ;call init fn + (. gen (invokeInterface ifn-type (new Method "invoke" obj-type + (arg-types (count ptypes))))) + ;expecting [[super-ctor-args] state] returned + (. gen dup) + (. gen push 0) + (. gen (invokeStatic rt-type nth-method)) + (. gen storeLocal local) + + (. gen (loadThis)) + (. gen dupX1) + (dotimes i (count super-pclasses) + (. gen loadLocal local) + (. gen push i) + (. gen (invokeStatic rt-type nth-method)) + (. clojure.lang.Compiler$HostExpr (emitUnboxArg nil gen (nth super-pclasses i)))) + (. gen (invokeConstructor super-type super-m)) + + (if state + (do + (. gen push 1) + (. gen (invokeStatic rt-type nth-method)) + (. gen (putField ctype state-name obj-type))) + (. gen pop)) + + (. gen goTo end-label) + ;no init found + (. gen mark no-init-label) + (. gen (throwException ex-type (str init-name " not defined"))) + (. gen mark end-label)) + (if (= pclasses super-pclasses) + (do + (. gen (loadThis)) + (. gen (loadArgs)) + (. gen (invokeConstructor super-type super-m))) + (throw (new Exception ":init not specified, but ctor and super ctor args differ")))) + + (. gen (returnValue)) + (. gen (endMethod)) + ;factory + (when factory + (let [fm (new Method factory-name ctype ptypes) + gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC)) + fm nil nil cv)] + (. gen (visitCode)) + (. gen newInstance ctype) + (. gen dup) + (. gen (loadArgs)) + (. gen (invokeConstructor ctype m)) + (. gen (returnValue)) + (. gen (endMethod)))))) + + ;add methods matching supers', if no fn -> call super + (let [mm (non-private-methods super)] + (doseq #^java.lang.reflect.Method meth (vals mm) + (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) + (fn [gen m] + (. gen (loadThis)) + ;push args + (. gen (loadArgs)) + ;call super + (. gen (visitMethodInsn (. Opcodes INVOKESPECIAL) + (. super-type (getInternalName)) + (. m (getName)) + (. m (getDescriptor))))))) + ;add methods matching interfaces', if no fn -> throw + (doseq #^Class iface interfaces + (doseq #^java.lang.reflect.Method meth (. iface (getMethods)) + (when-not (contains? mm (method-sig meth)) + (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) + (fn [gen m] + (. gen (throwException ex-type (. m (getName))))))))) + ;extra methods + (doseq [mname pclasses rclass :as msig] methods + (emit-forwarding-method (str mname) pclasses rclass + (fn [gen m] + (. gen (throwException ex-type (. m (getName)))))))) + + ;main + (when main + (let [m (. Method getMethod "void main (String[])") + gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC)) + m nil nil cv) + no-main-label (. gen newLabel) + end-label (. gen newLabel)] + (. gen (visitCode)) + + (emit-get-var gen main-name) + (. gen dup) + (. gen ifNull no-main-label) + (. gen loadArgs) + (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.ISeq seq(Object)")))) + (. gen (invokeInterface ifn-type (new Method "applyTo" obj-type + (into-array [iseq-type])))) + (. gen pop) + (. gen goTo end-label) + ;no main found + (. gen mark no-main-label) + (. gen (throwException ex-type (str main-name " not defined"))) + (. gen mark end-label) + (. gen (returnValue)) + (. gen (endMethod)))) + ;field exposers + (doseq [f {getter :get setter :set}] exposes + (let [fld (.getField super (str f)) + ftype (totype (.getType fld))] + (when getter + (let [m (new Method (str getter) ftype (to-types [])) + gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)] + (. gen (visitCode)) + (. gen loadThis) + (. gen getField ctype (str f) ftype) + (. gen (returnValue)) + (. gen (endMethod)))) + (when setter + (let [m (new Method (str setter) (. Type VOID_TYPE) (into-array [ftype])) + gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)] + (. gen (visitCode)) + (. gen loadThis) + (. gen loadArgs) + (. gen putField ctype (str f) ftype) + (. gen (returnValue)) + (. gen (endMethod)))))) + ;finish class def + (. cv (visitEnd)) + {:name name :bytecode (. cv (toByteArray))})) + +(defn gen-and-load-class + "Generates and immediately loads the bytecode for the specified + class. Note that a class generated this way can be loaded only once + - the JVM supports only one class with a given name per + classloader. Subsequent to generation you can import it into any + desired namespaces just like any other class. See gen-class for a + description of the options." + + [name & options] + (let [{:keys [name bytecode]} + (apply gen-class (str name) options)] + (.. clojure.lang.RT ROOT_CLASSLOADER (defineClass (str name) bytecode)))) + +(defn gen-and-save-class + "Generates the bytecode for the named class and stores in a .class + file in a subpath of the supplied path, the directories for which + must already exist. See gen-class for a description of the options" + + [path name & options] + (let [{:keys [name bytecode]} (apply gen-class (str name) options) + file (java.io.File. path (str (. name replace \. (. java.io.File separatorChar)) ".class"))] + (.createNewFile file) + (with-open f (java.io.FileOutputStream. file) + (.write f bytecode)))) + +(comment +;usage +(gen-class + package-qualified-name + ;all below are optional + :extends aclass + :implements [interface ...] + :constructors {[param-types] [super-param-types], } + :methods [[name [param-types] return-type], ] + :main boolean + :factory name + :state name + :init name + :exposes {protected-field {:get name :set name}, }) + +;(gen-and-load-class +(clojure/gen-and-save-class + "/Users/rich/Downloads" + 'fred.lucy.Ethel + :extends clojure.lang.Box ;APersistentMap + :implements [clojure.lang.IPersistentMap] + :state 'state + ;:constructors {[Object] [Object]} + ;:init 'init + :main true + :factory 'create + :methods [['foo [Object] Object] + ['foo [] Object]] + :exposes {'val {:get 'getVal :set 'setVal}}) + +(in-ns 'fred.lucy.Ethel__2276) +(clojure/refer 'clojure :exclude '(assoc seq count cons)) +(defn init [n] [[] n]) +(defn foo + ([this] :foo) + ([this x] x)) +(defn main [x y] (println x y)) +(in-ns 'user) +(def ethel (new fred.lucy.Ethel__2276 42)) +(def ethel (fred.lucy.Ethel__2276.create 21)) +(fred.lucy.Ethel__2276.main (into-array ["lucy" "ricky"])) +(.state ethel) +(.foo ethel 7) +(.foo ethel) +(.getVal ethel) +(.setVal ethel 12) + +(gen-class org.clojure.MyComparator :implements [Comparator]) +(in-ns 'org.clojure.MyComparator) +(defn compare [this x y] ...) + +(load-file "/Users/rich/dev/clojure/src/genclass.clj") + +(clojure/gen-and-save-class "/Users/rich/dev/clojure/gen/" + 'org.clojure.ClojureServlet + :extends javax.servlet.http.HttpServlet) + +) diff --git a/tests/examplefiles/genshi_example.xml+genshi b/tests/examplefiles/genshi_example.xml+genshi new file mode 100644 index 0000000..8576b04 --- /dev/null +++ b/tests/examplefiles/genshi_example.xml+genshi @@ -0,0 +1,193 @@ + + + + + $title + + + + +

+ + + (${v or 'No'} match${v != 1 and 'es' or ''}) + + +
+

$title ${num_matches(len(tickets))}

+ +
+
+ Filters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+   + + +
+
+ +

+ + + + +

+ +

+ + +

+ +
+ + + +
+
+
+ + + + + +
+ Note: See TracQuery + for help on using queries. +
+ +
+ + diff --git a/tests/examplefiles/genshitext_example.genshitext b/tests/examplefiles/genshitext_example.genshitext new file mode 100644 index 0000000..b82708d --- /dev/null +++ b/tests/examplefiles/genshitext_example.genshitext @@ -0,0 +1,33 @@ + ## a comment + +\## not a comment + +#if foo + ${bar} +#endif + +The answer is: +#choose + #when 0 == 1 + 0 + #end + #when 1 == 1 + 1 + #end + #otherwise + 2 + #end +#end -- comment about choose + +#for item in items + * ${item} +#end + +#def greeting(name) + Hello, ${name}! +#end +${greeting('world')} + +#with y=7; z=x+10 + $x $y $z +#end diff --git a/tests/examplefiles/glsl.frag b/tests/examplefiles/glsl.frag new file mode 100644 index 0000000..132b035 --- /dev/null +++ b/tests/examplefiles/glsl.frag @@ -0,0 +1,7 @@ +/* Fragment shader */ +void main() +{ + gl_FragColor[0] = gl_FragCoord[0] / 400.0; + gl_FragColor[1] = gl_FragCoord[1] / 400.0; + gl_FragColor[2] = 1.0; +} diff --git a/tests/examplefiles/glsl.vert b/tests/examplefiles/glsl.vert new file mode 100644 index 0000000..23dc6a6 --- /dev/null +++ b/tests/examplefiles/glsl.vert @@ -0,0 +1,13 @@ +/* Vertex shader */ +uniform float waveTime; +uniform float waveWidth; +uniform float waveHeight; + +void main(void) +{ + vec4 v = vec4(gl_Vertex); + + v.z = sin(waveWidth * v.x + waveTime) * cos(waveWidth * v.y + waveTime) * waveHeight; + + gl_Position = gl_ModelViewProjectionMatrix * v; +} diff --git a/tests/examplefiles/html+php_faulty.php b/tests/examplefiles/html+php_faulty.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/tests/examplefiles/html+php_faulty.php @@ -0,0 +1 @@ + puts < nil +irb(main):005:0> diff --git a/tests/examplefiles/jinjadesignerdoc.rst b/tests/examplefiles/jinjadesignerdoc.rst new file mode 100644 index 0000000..b4b6c44 --- /dev/null +++ b/tests/examplefiles/jinjadesignerdoc.rst @@ -0,0 +1,713 @@ +====================== +Designer Documentation +====================== + +This part of the Jinja documentaton is meant for template designers. + +Basics +====== + +The Jinja template language is designed to strike a balance between content +and application logic. Nevertheless you can use a python like statement +language. You don't have to know how Python works to create Jinja templates, +but if you know it you can use some additional statements you may know from +Python. + +Here is a small example template: + +.. sourcecode:: html+jinja + + + + + My Webpage + + + + +

My Webpage

+ {{ variable }} + + + +This covers the default settings. The application developer might have changed +the syntax from ``{% foo %}`` to ``<% foo %>`` or something similar. This +documentation just covers the default values. + +A variable looks like ``{{ foobar }}`` where foobar is the variable name. Inside +of statements (``{% some content here %}``) variables are just normal names +without the braces around it. In fact ``{{ foobar }}`` is just an alias for +the statement ``{% print foobar %}``. + +Variables are coming from the context provided by the application. Normally there +should be a documentation regarding the context contents but if you want to know +the content of the current context, you can add this to your template: + +.. sourcecode:: html+jinja + +
{{ debug()|e }}
+ +A context isn't flat which means that each variable can has subvariables, as long +as it is representable as python data structure. You can access attributes of +a variable using the dot and bracket operators. The following examples show +this: + +.. sourcecode:: jinja + + {{ user.username }} + is the same as + {{ user['username'] }} + you can also use a variable to access an attribute: + {{ users[current_user].username }} + If you have numerical indices you have to use the [] syntax: + {{ users[0].username }} + +Filters +======= + +In the examples above you might have noticed the pipe symbols. Pipe symbols tell +the engine that it has to apply a filter on the variable. Here is a small example: + +.. sourcecode:: jinja + + {{ variable|replace('foo', 'bar')|escape }} + +If you want, you can also put whitespace between the filters. + +This will look for a variable `variable`, pass it to the filter `replace` +with the arguments ``'foo'`` and ``'bar'``, and pass the result to the filter +`escape` that automatically XML-escapes the value. The `e` filter is an alias for +`escape`. Here is the complete list of supported filters: + +[[list_of_filters]] + +.. admonition:: note + + Filters have a pretty low priority. If you want to add fitered values + you have to put them into parentheses. The same applies if you want to access + attributes: + + .. sourcecode:: jinja + + correct: + {{ (foo|filter) + (bar|filter) }} + wrong: + {{ foo|filter + bar|filter }} + + correct: + {{ (foo|filter).attribute }} + wrong: + {{ foo|filter.attribute }} + +Tests +===== + +You can use the `is` operator to perform tests on a value: + +.. sourcecode:: jinja + + {{ 42 is numeric }} -> true + {{ "foobar" is numeric }} -> false + {{ 'FOO' is upper }} -> true + +These tests are especially useful when used in `if` conditions. + +[[list_of_tests]] + +Global Functions +================ + +Test functions and filter functions live in their own namespace. Global +functions not. They behave like normal objects in the context. Beside the +functions added by the application or framewhere there are two functions +available per default: + +`range` + + Works like the python `range function`_ just that it doesn't support + ranges greater than ``1000000``. + +`debug` + + Function that outputs the contents of the context. + +Loops +===== + +To iterate over a sequence, you can use the `for` loop. It basically looks like a +normal Python `for` loop and works pretty much the same: + +.. sourcecode:: html+jinja + +

Members

+
    + {% for user in users %} +
  • {{ loop.index }} / {{ loop.length }} - {{ user.username|escape }}
  • + {% else %} +
  • no users found
  • + {% endfor %} +
+ +*Important* Contrary to Python is the optional ``else`` block only +executed if there was no iteration because the sequence was empty. + +Inside of a `for` loop block you can access some special variables: + ++----------------------+----------------------------------------+ +| Variable | Description | ++======================+========================================+ +| `loop.index` | The current iteration of the loop. | ++----------------------+----------------------------------------+ +| `loop.index0` | The current iteration of the loop, | +| | starting counting by 0. | ++----------------------+----------------------------------------+ +| `loop.revindex` | The number of iterations from the end | +| | of the loop. | ++----------------------+----------------------------------------+ +| `loop.revindex0` | The number of iterations from the end | +| | of the loop, starting counting by 0. | ++----------------------+----------------------------------------+ +| `loop.first` | True if first iteration. | ++----------------------+----------------------------------------+ +| `loop.last` | True if last iteration. | ++----------------------+----------------------------------------+ +| `loop.even` | True if current iteration is even. | ++----------------------+----------------------------------------+ +| `loop.odd` | True if current iteration is odd. | ++----------------------+----------------------------------------+ +| `loop.length` | Total number of items in the sequence. | ++----------------------+----------------------------------------+ +| `loop.parent` | The context of the parent loop. | ++----------------------+----------------------------------------+ + +Loops also support recursion. Let's assume you have a sitemap where each item +might have a number of child items. A template for that could look like this: + +.. sourcecode:: html+jinja + +

Sitemap +
    + {% for item in sitemap recursive %} +
  • {{ item.title|e }} + {% if item.children %}
      {{ loop(item.children) }}
    {% endif %}
  • + {% endfor %} +
+ +What happens here? Basically the first thing that is different to a normal +loop is the additional ``recursive`` modifier in the `for`-loop declaration. +It tells the template engine that we want recursion. If recursion is enabled +the special `loop` variable is callable. If you call it with a sequence it will +automatically render the loop at that position with the new sequence as argument. + +Cycling +======= + +Sometimes you might want to have different text snippets for each row in a list, +for example to have alternating row colors. You can easily do this by using the +``{% cycle %}`` tag: + +.. sourcecode:: html+jinja + +
    + {% for message in messages %} +
  • {{ message|e }}
  • + {% endfor %} +
+ +Each time Jinja encounters a `cycle` tag it will cycle through the list +of given items and return the next one. If you pass it one item jinja assumes +that this item is a sequence from the context and uses this: + +.. sourcecode:: html+jinja + +
  • ...
  • + +Conditions +========== + +Jinja supports Python-like `if` / `elif` / `else` constructs: + +.. sourcecode:: jinja + + {% if user.active %} + user {{ user.name|e }} is active. + {% elif user.deleted %} + user {{ user.name|e }} was deleted some time ago. + {% else %} + i don't know what's wrong with {{ user.username|e }} + {% endif %} + +If the user is active the first block is rendered. If not and the user was +deleted the second one, in all other cases the third one. + +You can also use comparison operators: + +.. sourcecode:: html+jinja + + {% if amount < 0 %} + {{ amount }} + {% else %} + {{ amount }} + {% endif %} + +.. admonition:: Note + + Of course you can use `or` / `and` and parentheses to create more complex + conditions, but usually the logic is already handled in the application and + you don't have to create such complex constructs in the template code. However + in some situations it might be a good thing to have the abilities to create + them. + +Operators +========= + +Inside ``{{ variable }}`` blocks, `if` conditions and many other parts you can +can use expressions. In expressions you can use any of the following operators: + + ======= =================================================================== + ``+`` add the right operand to the left one. + ``{{ 1 + 2 }}`` would return ``3``. + ``-`` subtract the right operand from the left one. + ``{{ 1 - 1 }}`` would return ``0``. + ``/`` divide the left operand by the right one. + ``{{ 1 / 2 }}`` would return ``0.5``. + ``*`` multiply the left operand with the right one. + ``{{ 2 * 2 }}`` would return ``4``. + ``**`` raise the left operand to the power of the right + operand. ``{{ 2**3 }}`` would return ``8``. + ``in`` perform sequence membership test. ``{{ 1 in [1,2,3] }}`` would + return true. + ``is`` perform a test on the value. See the section about + tests for more information. + ``|`` apply a filter on the value. See the section about + filters for more information. + ``and`` return true if the left and the right operand is true. + ``or`` return true if the left or the right operand is true. + ``not`` negate a statement (see below) + ``()`` call a callable: ``{{ user.get_username() }}``. Inside of the + parentheses you can use variables: ``{{ user.get(username) }}``. + ======= =================================================================== + +Note that there is no support for any bit operations or something similar. + +* special note regarding `not`: The `is` and `in` operators support negation + using an infix notation too: ``foo is not bar`` and ``foo not in bar`` + instead of ``not foo is bar`` and ``not foo in bar``. All other expressions + require a prefix notation: ``not (foo and bar)``. + +Boolean Values +============== + +In If-Conditions Jinja performs a boolean check. All empty values (eg: empty +lists ``[]``, empty dicts ``{}`` etc) evaluate to `false`. Numbers that are +equal to `0`/`0.00` are considered `false` too. The boolean value of other +objects depends on the behavior the application developer gave it. Usually +items are `true`. + +Here some examples that should explain it: + +.. sourcecode:: jinja + + {% if [] %} + will always be false because it's an empty list + + {% if {} %} + false too. + + {% if ['foo'] %} + this is true. Because the list is not empty. + + {% if "foobar" %} + this is also true because the string is not empty. + +Slicing +======= + +Some objects support slicing operations. For example lists: + +.. sourcecode:: jinja + + {% for item in items[:5] %} + This will only iterate over the first 5 items of the list + + {% for item in items[5:10] %} + This will only iterate from item 5 to 10. + + {% for item in items[:10:2] %} + This will only yield items from start to ten and only returing + even items. + +For more informations about slicing have a look at the `slicing chapter`_ +in the "Dive into Python" e-book. + +Macros +====== + +If you want to use a partial template in more than one place, you might want to +create a macro from it: + +.. sourcecode:: html+jinja + + {% macro show_user user %} +

    {{ user.name|e }}

    +
    + {{ user.description }} +
    + {% endmacro %} + +Now you can use it from everywhere in the code by passing it an item: + +.. sourcecode:: jinja + + {% for user in users %} + {{ show_user(user) }} + {% endfor %} + +You can also specify more than one value: + +.. sourcecode:: html+jinja + + {% macro show_dialog title, text %} +
    +

    {{ title|e }}

    +
    {{ text|e }}
    +
    + {% endmacro %} + + {{ show_dialog('Warning', 'something went wrong i guess') }} + +Inheritance +=========== + +The most powerful part of Jinja is template inheritance. Template inheritance +allows you to build a base "skeleton" template that contains all the common +elements of your site and defines **blocks** that child templates can override. + +Sounds complicated but is very basic. It's easiest to understand it by starting +with an example. + +Base Template +------------- + +This template, which we'll call ``base.html``, defines a simple HTML skeleton +document that you might use for a simple two-column page. It's the job of +"child" templates to fill the empty blocks with content: + +.. sourcecode:: html+jinja + + + + + + {% block title %}{% endblock %} - My Webpage + {% block html_head %}{% endblock %} + + +
    + {% block content %}{% endblock %} +
    + + + + +In this example, the ``{% block %}`` tags define four blocks that child templates +can fill in. All the `block` tag does is to tell the template engine that a +child template may override those portions of the template. + +Child Template +-------------- + +A child template might look like this: + +.. sourcecode:: html+jinja + + {% extends "base.html" %} + {% block title %}Index{% endblock %} + + {% block html_head %} + + {% endblock %} + + {% block content %} +

    Index

    +

    + Welcome on my awsome homepage. +

    + {% endblock %} + +The ``{% extends %}`` tag is the key here. It tells the template engine that +this template "extends" another template. When the template system evaluates +this template, first it locates the parent. + +The filename of the template depends on the template loader. For example the +``FileSystemLoader`` allows you to access other templates by giving the +filename. You can access templates in subdirectories with an slash: + +.. sourcecode:: jinja + + {% extends "layout/default.html" %} + +But this behavior can depend on the application using Jinja. + +Note that since the child template didn't define the ``footer`` block, the +value from the parent template is used instead. + +.. admonition:: Note + + You can't define multiple ``{% block %}`` tags with the same name in the + same template. This limitation exists because a block tag works in "both" + directions. That is, a block tag doesn't just provide a hole to fill - it + also defines the content that fills the hole in the *parent*. If there were + two similarly-named ``{% block %}`` tags in a template, that template's + parent wouldn't know which one of the blocks' content to use. + +Template Inclusion +================== + +You can load another template at a given position using ``{% include %}``. +Usually it's a better idea to use inheritance but if you for example want to +load macros, `include` works better than `extends`: + +.. sourcecode:: jinja + + {% include "myhelpers.html" %} + {{ my_helper("foo") }} + +If you define a macro called ``my_helper`` in ``myhelpers.html``, you can now +use it from the template as shown above. + +Filtering Blocks +================ + +Sometimes it could be a good idea to filter a complete block of text. For +example, if you want to escape some html code: + +.. sourcecode:: jinja + + {% filter escape %} + + goes here + + {% endfilter %} + +Of course you can chain filters too: + +.. sourcecode:: jinja + + {% filter lower|escape %} + SOME TEXT + {% endfilter %} + +returns ``"<b>some text</b>"``. + +Defining Variables +================== + +You can also define variables in the namespace using the ``{% set %}`` tag: + +.. sourcecode:: jinja + + {% set foo = 'foobar' %} + {{ foo }} + +This should ouput ``foobar``. + +Scopes +====== + +Jinja has multiple scopes. A scope is something like a new transparent foil on +a stack of foils. You can only write to the outermost foil but read all of them +since you can look through them. If you remove the top foil all data on that +foil disappears. Some tags in Jinja add a new layer to the stack. Currently +these are `block`, `for`, `macro` and `filter`. This means that variables and +other elements defined inside a macro, loop or some of the other tags listed +above will be only available in that block. Here an example: + +.. sourcecode:: jinja + + {% macro angryhello name %} + {% set angryname = name|upper %} + Hello {{ name }}. Hello {{ name }}! + HELLO {{ angryname }}!!!!!!111 + {% endmacro %} + +The variable ``angryname`` just exists inside the macro, not outside it. + +Defined macros appear on the context as variables. Because of this, they are +affected by the scoping too. A macro defined inside of a macro is just available +in those two macros (the macro itself and the macro it's defined in). For `set` +and `macro` two additional rules exist: If a macro is defined in an extended +template but outside of a visible block (thus outside of any block) will be +available in all blocks below. This allows you to use `include` statements to +load often used macros at once. + +Undefined Variables +=================== + +If you have already worked with python you probably know about the fact that +undefined variables raise an exception. This is different in Jinja. There is a +special value called `undefined` that represents values that do not exist. + +This special variable works complete different from any variables you maybe +know. If you print it using ``{{ variable }}`` it will not appear because it's +literally empty. If you try to iterate over it, it will work. But no items +are returned. Comparing this value to any other value results in `false`. +Even if you compare it to itself: + +.. sourcecode:: jinja + + {{ undefined == undefined }} + will return false. Not even undefined is undefined :) + Use `is defined` / `is not defined`: + + {{ undefined is not defined }} + will return true. + +There are also some additional rules regarding this special value. Any +mathematical operators (``+``, ``-``, ``*``, ``/``) return the operand +as result: + +.. sourcecode:: jinja + + {{ undefined + "foo" }} + returns "foo" + + {{ undefined - 42 }} + returns 42. Note: not -42! + +In any expression `undefined` evaluates to `false`. It has no length, all +attribute calls return undefined, calling too: + +.. sourcecode:: jinja + + {{ undefined.attribute().attribute_too[42] }} + still returns `undefined`. + +Escaping +======== + +Sometimes you might want to add Jinja syntax elements into the template +without executing them. In that case you have quite a few possibilities. + +For small parts this might be a good way: + +.. sourcecode:: jinja + + {{ "{{ foo }} is variable syntax and {% foo %} is block syntax" }} + +When you have multiple elements you can use the ``raw`` block: + +.. sourcecode:: jinja + + {% raw %} + Filtering blocks works like this in Jinja: + {% filter escape %} + + goes here + + {% endfilter %} + {% endraw %} + +Reserved Keywords +================= + +Jinja has some keywords you cannot use a variable names. This limitation +exists to make look coherent. Syntax highlighters won't mess things up and +you will don't have unexpected output. + +The following keywords exist and cannot be used as identifiers: + + `and`, `block`, `cycle`, `elif`, `else`, `endblock`, `endfilter`, + `endfor`, `endif`, `endmacro`, `endraw`, `endtrans`, `extends`, `filter`, + `for`, `if`, `in`, `include`, `is`, `macro`, `not`, `or`, `pluralize`, + `raw`, `recursive`, `set`, `trans` + +If you want to use such a name you have to prefix or suffix it or use +alternative names: + +.. sourcecode:: jinja + + {% for macro_ in macros %} + {{ macro_('foo') }} + {% endfor %} + +If future Jinja releases add new keywords those will be "light" keywords which +means that they won't raise an error for several releases but yield warnings +on the application side. But it's very unlikely that new keywords will be +added. + +Internationalization +==================== + +If the application is configured for i18n, you can define translatable blocks +for translators using the `trans` tag or the special underscore function: + +.. sourcecode:: jinja + + {% trans %} + this is a translatable block + {% endtrans %} + + {% trans "This is a translatable string" %} + + {{ _("This is a translatable string") }} + +The latter one is useful if you want translatable arguments for filters etc. + +If you want to have plural forms too, use the `pluralize` block: + +.. sourcecode:: jinja + + {% trans users=users %} + One user found. + {% pluralize %} + {{ users }} users found. + {% endtrans %} + + {% trans first=(users|first).username|escape, user=users|length %} + one user {{ first }} found. + {% pluralize users %} + {{ users }} users found, the first one is called {{ first }}. + {% endtrans %} + +If you have multiple arguments, the first one is assumed to be the indicator (the +number that is used to determine the correct singular or plural form. If you +don't have the indicator variable on position 1 you have to tell the `pluralize` +tag the correct variable name. + +Inside translatable blocks you cannot use blocks or expressions (however you can +still use the ``raw`` block which will work as expected). The variable +print syntax (``{{ variablename }}``) is the only way to insert the variables +defined in the ``trans`` header. Filters must be applied in the header. + +.. admonition:: note + + Please make sure that you always use pluralize blocks where required. + Many languages have more complex plural forms than the English language. + + Never try to workaround that issue by using something like this: + + .. sourcecode:: jinja + + {% if count != 1 %} + {{ count }} users found. + {% else %} + one user found. + {% endif %} + +.. _slicing chapter: http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice +.. _range function: http://docs.python.org/tut/node6.html#SECTION006300000000000000000 diff --git a/tests/examplefiles/lighttpd_config.conf b/tests/examplefiles/lighttpd_config.conf new file mode 100644 index 0000000..8475f37 --- /dev/null +++ b/tests/examplefiles/lighttpd_config.conf @@ -0,0 +1,13 @@ +fastcgi.server = ( ".php" => (( + "bin-path" => "/path/to/php-cgi", + "socket" => "/tmp/php.socket", + "max-procs" => 2, + "bin-environment" => ( + "PHP_FCGI_CHILDREN" => "16", + "PHP_FCGI_MAX_REQUESTS" => "10000" + ), + "bin-copy-environment" => ( + "PATH", "SHELL", "USER" + ), + "broken-scriptfilename" => "enable" + ))) diff --git a/tests/examplefiles/linecontinuation.py b/tests/examplefiles/linecontinuation.py new file mode 100644 index 0000000..2a41c31 --- /dev/null +++ b/tests/examplefiles/linecontinuation.py @@ -0,0 +1,47 @@ +apple.filter(x, y) +apple.\ + filter(x, y) + +1 \ + . \ + __str__ + +from os import path +from \ + os \ + import \ + path + +import os.path as something + +import \ + os.path \ + as \ + something + +class \ + Spam: + pass + +class Spam: pass + +class Spam(object): + pass + +class \ + Spam \ + ( + object + ) \ + : + pass + + +def \ + spam \ + ( \ + ) \ + : \ + pass + + diff --git a/tests/examplefiles/ltmain.sh b/tests/examplefiles/ltmain.sh new file mode 100644 index 0000000..5b5f845 --- /dev/null +++ b/tests/examplefiles/ltmain.sh @@ -0,0 +1,2849 @@ +# ltmain.sh - Provide generalized library-building support services. +# NOTE: Changing this file will not affect anything until you rerun configure. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# Originally by Gordon Matzigkeit , 1996 +# +# 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. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +basename="s,^.*/,,g" + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + +# define SED for historic ltconfig's generated by Libtool 1.3 +test -z "$SED" && SED=sed + +# The name of this program: +progname=`echo "$progpath" | $SED $basename` +modename="$progname" + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 + +PROGRAM=ltmain.sh +PACKAGE=libtool +VERSION=1.5.22 +TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)" + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes. +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi +# Same for EGREP, and just to be sure, do LTCC as well +if test "X$EGREP" = X ; then + EGREP=egrep +fi +if test "X$LTCC" = X ; then + LTCC=${CC-gcc} +fi + +# Check that we have a working $echo. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then + # Yippee, $echo works! + : +else + # Restart under the correct shell, and then maybe $echo will work. + exec $SHELL "$progpath" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat <&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit $EXIT_FAILURE +fi + +# Global variables. +mode=$default_mode +nonopt= +prev= +prevopt= +run= +show="$echo" +show_help= +execute_dlfiles= +duplicate_deps=no +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" + +if test -z "$max_cmd_len"; then + i=0 + testring="ABCD" + new_result= + + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while (test "X"`$SHELL $0 --fallback-echo "X$testring" 2>/dev/null` \ + = "XX$testring") >/dev/null 2>&1 && + new_result=`expr "X$testring" : ".*" 2>&1` && + max_cmd_len="$new_result" && + test "$i" != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + testring="$testring$testring" + done + testring= + # Add a significant safety factor because C++ compilers can tack on massive + # amounts of additional arguments before passing them to the linker. + # It appears as though 1/2 is a usable value. + max_cmd_len=`expr $max_cmd_len \/ 2` +fi + +##################################### +# Shell function definitions: +# This seems to be the best place for them + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $mkdir "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || { + $echo "cannot create temporary directory \`$my_tmpdir'" 1>&2 + exit $EXIT_FAILURE + } + fi + + $echo "X$my_tmpdir" | $Xsed +} + + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +func_win32_libid () +{ + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ + $EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then + win32_nmres=`eval $NM -f posix -A $1 | \ + $SED -n -e '1,100{/ I /{s,.*,import,;p;q;};}'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $echo $win32_libid_type +} + + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + CC_quoted="$CC_quoted $arg" + done + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + CC_quoted="$CC_quoted $arg" + done + # user sometimes does CC=-gcc so we need to match that to 'gcc' + trimedcc=`echo ${CC} | $SED -e "s/${host}-//g"` + # and sometimes libtool has CC=-gcc but user does CC=gcc + extendcc=${host}-${CC} + # and sometimes libtool has CC=-gcc but user has CC=-gcc + # (Gentoo-specific hack because we always export $CHOST) + mungedcc=${CHOST-${host}}-${trimedcc} + case "$@ " in + "cc "* | " cc "* | "${host}-cc "* | " ${host}-cc "*|\ + "gcc "* | " gcc "* | "${host}-gcc "* | " ${host}-gcc "*) + tagname=CC + break ;; + "$trimedcc "* | " $trimedcc "* | "`$echo $trimedcc` "* | " `$echo $trimedcc` "*|\ + "$extendcc "* | " $extendcc "* | "`$echo $extendcc` "* | " `$echo $extendcc` "*|\ + "$mungedcc "* | " $mungedcc "* | "`$echo $mungedcc` "* | " `$echo $mungedcc` "*|\ + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + $echo "$modename: unable to infer tagged configuration" + $echo "$modename: specify a tag with \`--tag'" 1>&2 + exit $EXIT_FAILURE +# else +# $echo "$modename: using $tagname tagged configuration" + fi + ;; + esac + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + + $show "(cd $f_ex_an_ar_dir && $AR x $f_ex_an_ar_oldlib)" + $run eval "(cd \$f_ex_an_ar_dir && $AR x \$f_ex_an_ar_oldlib)" || exit $? + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: ERROR: object name conflicts: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" 1>&2 + exit $EXIT_FAILURE + fi +} + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + my_status="" + + $show "${rm}r $my_gentop" + $run ${rm}r "$my_gentop" + $show "$mkdir $my_gentop" + $run $mkdir "$my_gentop" + my_status=$? + if test "$my_status" -ne 0 && test ! -d "$my_gentop"; then + exit $my_status + fi + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + my_xlib=`$echo "X$my_xlib" | $Xsed -e 's%^.*/%%'` + my_xdir="$my_gentop/$my_xlib" + + $show "${rm}r $my_xdir" + $run ${rm}r "$my_xdir" + $show "$mkdir $my_xdir" + $run $mkdir "$my_xdir" + exit_status=$? + if test "$exit_status" -ne 0 && test ! -d "$my_xdir"; then + exit $exit_status + fi + case $host in + *-darwin*) + $show "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + if test -z "$run"; then + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`$echo "X$darwin_archive" | $Xsed -e 's%^.*/%%'` + darwin_arches=`lipo -info "$darwin_archive" 2>/dev/null | $EGREP Architectures 2>/dev/null` + if test -n "$darwin_arches"; then + darwin_arches=`echo "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + $show "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + mkdir -p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + lipo -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $rm "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we have a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print| xargs basename | sort -u | $NL2SP` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` + lipo -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + ${rm}r unfat-$$ + cd "$darwin_orig_dir" + else + cd "$darwin_orig_dir" + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + fi # $run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + func_extract_archives_result="$my_oldobjs" +} +# End of Shell function definitions +##################################### + +# Darwin sucks +eval std_shrext=\"$shrext_cmds\" + +disable_libs=no + +# Parse our command line options once, thoroughly. +while test "$#" -gt 0 +do + arg="$1" + shift + + case $arg in + -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + execute_dlfiles) + execute_dlfiles="$execute_dlfiles $arg" + ;; + tag) + tagname="$arg" + preserve_args="${preserve_args}=$arg" + + # Check whether tagname contains only valid characters + case $tagname in + *[!-_A-Za-z0-9,/]*) + $echo "$progname: invalid tag name: $tagname" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $tagname in + CC) + # Don't test for the "default" C tag, as we know, it's there, but + # not specially marked. + ;; + *) + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$progpath" > /dev/null; then + taglist="$taglist $tagname" + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $progpath`" + else + $echo "$progname: ignoring unknown tag $tagname" 1>&2 + fi + ;; + esac + ;; + *) + eval "$prev=\$arg" + ;; + esac + + prev= + prevopt= + continue + fi + + # Have we seen a non-optional argument yet? + case $arg in + --help) + show_help=yes + ;; + + --version) + $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" + $echo + $echo "Copyright (C) 2005 Free Software Foundation, Inc." + $echo "This is free software; see the source for copying conditions. There is NO" + $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + exit $? + ;; + + --config) + ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $progpath + # Now print the configurations for the tags. + for tagname in $taglist; do + ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$progpath" + done + exit $? + ;; + + --debug) + $echo "$progname: enabling shell trace mode" + set -x + preserve_args="$preserve_args $arg" + ;; + + --dry-run | -n) + run=: + ;; + + --features) + $echo "host: $host" + if test "$build_libtool_libs" = yes; then + $echo "enable shared libraries" + else + $echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + $echo "enable static libraries" + else + $echo "disable static libraries" + fi + exit $? + ;; + + --finish) mode="finish" ;; + + --mode) prevopt="--mode" prev=mode ;; + --mode=*) mode="$optarg" ;; + + --preserve-dup-deps) duplicate_deps="yes" ;; + + --quiet | --silent) + show=: + preserve_args="$preserve_args $arg" + ;; + + --tag) + prevopt="--tag" + prev=tag + preserve_args="$preserve_args --tag" + ;; + --tag=*) + set tag "$optarg" ${1+"$@"} + shift + prev=tag + preserve_args="$preserve_args --tag" + ;; + + -dlopen) + prevopt="-dlopen" + prev=execute_dlfiles + ;; + + -*) + $echo "$modename: unrecognized option \`$arg'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + + *) + nonopt="$arg" + break + ;; + esac +done + +if test -n "$prevopt"; then + $echo "$modename: option \`$prevopt' requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE +fi + +case $disable_libs in +no) + ;; +shared) + build_libtool_libs=no + build_old_libs=yes + ;; +static) + build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` + ;; +esac + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +if test -z "$show_help"; then + + # Infer the operation mode. + if test -z "$mode"; then + $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 + $echo "*** Future versions of Libtool will require --mode=MODE be specified." 1>&2 + case $nonopt in + *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) + mode=link + for arg + do + case $arg in + -c) + mode=compile + break + ;; + esac + done + ;; + *db | *dbx | *strace | *truss) + mode=execute + ;; + *install*|cp|mv) + mode=install + ;; + *rm) + mode=uninstall + ;; + *) + # If we have no mode, but dlfiles were specified, then do execute mode. + test -n "$execute_dlfiles" && mode=execute + + # Just use the default operation mode. + if test -z "$mode"; then + if test -n "$nonopt"; then + $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 + else + $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 + fi + fi + ;; + esac + fi + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + $echo "$modename: unrecognized option \`-dlopen'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$modename --help --mode=$mode' for more information." + + # These modes are in order of execution frequency so that they run quickly. + case $mode in + # libtool compile mode + compile) + modename="$modename: compile" + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + if test -n "$libobj" ; then + $echo "$modename: you cannot specify \`-o' more than once" 1>&2 + exit $EXIT_FAILURE + fi + arg_mode=target + continue + ;; + + -static | -prefer-pic | -prefer-non-pic) + later="$later $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + lastarg="$lastarg $arg" + done + IFS="$save_ifs" + lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"` + + # Add the arguments to base_compile. + base_compile="$base_compile $lastarg" + continue + ;; + + * ) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` + + case $lastarg in + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, and some SunOS ksh mistreat backslash-escaping + # in scan sets (worked around with variable expansion), + # and furthermore cannot handle '|' '&' '(' ')' in scan sets + # at all, so we specify them separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + lastarg="\"$lastarg\"" + ;; + esac + + base_compile="$base_compile $lastarg" + done # for arg + + case $arg_mode in + arg) + $echo "$modename: you must specify an argument for -Xcompile" + exit $EXIT_FAILURE + ;; + target) + $echo "$modename: you must specify a target with \`-o'" 1>&2 + exit $EXIT_FAILURE + ;; + *) + # Get the name of the library object. + [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + xform='[cCFSifmso]' + case $libobj in + *.ada) xform=ada ;; + *.adb) xform=adb ;; + *.ads) xform=ads ;; + *.asm) xform=asm ;; + *.c++) xform=c++ ;; + *.cc) xform=cc ;; + *.ii) xform=ii ;; + *.class) xform=class ;; + *.cpp) xform=cpp ;; + *.cxx) xform=cxx ;; + *.f90) xform=f90 ;; + *.for) xform=for ;; + *.java) xform=java ;; + esac + + libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` + + case $libobj in + *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; + *) + $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -static) + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + qlibobj=`$echo "X$libobj" | $Xsed -e "$sed_quote_subst"` + case $qlibobj in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qlibobj="\"$qlibobj\"" ;; + esac + test "X$libobj" != "X$qlibobj" \ + && $echo "X$libobj" | grep '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && $echo "$modename: libobj name \`$libobj' may not contain shell special characters." + objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` + xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$obj"; then + xdir= + else + xdir=$xdir/ + fi + lobj=${xdir}$objdir/$objname + + if test -z "$base_compile"; then + $echo "$modename: you must specify a compilation command" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + $run $rm $removelist + trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + removelist="$removelist $output_obj $lockfile" + trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $run ln "$srcfile" "$lockfile" 2>/dev/null; do + $show "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $echo "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + $echo "$srcfile" > "$lockfile" + fi + + if test -n "$fix_srcfile_path"; then + eval srcfile=\"$fix_srcfile_path\" + fi + qsrcfile=`$echo "X$srcfile" | $Xsed -e "$sed_quote_subst"` + case $qsrcfile in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qsrcfile="\"$qsrcfile\"" ;; + esac + + $run $rm "$libobj" "${libobj}T" + + # Create a libtool object file (analogous to a ".la" file), + # but don't create it if we're doing a dry run. + test -z "$run" && cat > ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + $show "$mv $output_obj $lobj" + if $run $mv $output_obj $lobj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the PIC object to the libtool object file. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + $show "$mv $output_obj $obj" + if $run $mv $output_obj $obj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the non-PIC object the libtool object file. + # Only append if the libtool object file exists. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T <&2 + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + else + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + fi + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test + ;; + *) qarg=$arg ;; + esac + libtool_args="$libtool_args $qarg" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + compile_command="$compile_command @OUTPUT@" + finalize_command="$finalize_command @OUTPUT@" + ;; + esac + + case $prev in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + compile_command="$compile_command @SYMFILE@" + finalize_command="$finalize_command @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + if test ! -f "$arg"; then + $echo "$modename: symbol file \`$arg' does not exist" + exit $EXIT_FAILURE + fi + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat $save_arg` + do +# moreargs="$moreargs $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit $EXIT_FAILURE + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit $EXIT_FAILURE + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + done + else + $echo "$modename: link input file \`$save_arg' does not exist" + exit $EXIT_FAILURE + fi + arg=$save_arg + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit $EXIT_FAILURE + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + xcompiler) + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + xlinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $wl$qarg" + prev= + compile_command="$compile_command $wl$qarg" + finalize_command="$finalize_command $wl$qarg" + continue + ;; + xcclinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + darwin_framework|darwin_framework_skip) + test "$prev" = "darwin_framework" && compiler_flags="$compiler_flags $arg" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + prev= + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 + continue + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: more than one -exported-symbols argument is not allowed" + exit $EXIT_FAILURE + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework|-arch|-isysroot) + case " $CC " in + *" ${arg} ${1} "* | *" ${arg} ${1} "*) + prev=darwin_framework_skip ;; + *) compiler_flags="$compiler_flags $arg" + prev=darwin_framework ;; + esac + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + ;; + esac + continue + ;; + + -L*) + dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 + absdir="$dir" + notinst_path="$notinst_path $dir" + fi + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "*) ;; + *) + deplibs="$deplibs -L$dir" + lib_search_path="$lib_search_path $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + testbindir=`$echo "X$dir" | $Xsed -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + *) dllsearchpath="$dllsearchpath:$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + deplibs="$deplibs -framework System" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + deplibs="$deplibs $arg" + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + -model) + compile_command="$compile_command $arg" + compiler_flags="$compiler_flags $arg" + finalize_command="$finalize_command $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + compiler_flags="$compiler_flags $arg" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # -64, -mips[0-9] enable 64-bit mode on the SGI compiler + # -r[0-9][0-9]* specifies the processor on the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler + # +DA*, +DD* enable 64-bit mode on the HP compiler + # -q* pass through compiler args for the IBM compiler + # -m* pass through architecture-specific compiler args for GCC + # -m*, -t[45]*, -txscale* pass through architecture-specific + # compiler args for GCC + # -pg pass through profiling flag for GCC + # @file GCC response files + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*|-pg| \ + -t[45]*|-txscale*|@*) + + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + compiler_flags="$compiler_flags $arg" + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + # The PATH hackery in wrapper scripts is required on Windows + # in order for the loader to find any dlls it needs. + $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 + $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit $EXIT_FAILURE + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; + + -static) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Wl,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $wl$flag" + linker_flags="$linker_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # Some other compiler flag. + -* | +*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + + *.$objext) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit $EXIT_FAILURE + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit $EXIT_FAILURE + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs="$deplibs $arg" + old_deplibs="$old_deplibs $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles="$dlfiles $arg" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles="$dlprefiles $arg" + prev= + else + deplibs="$deplibs $arg" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + done # argument parsing loop + + if test -n "$prev"; then + $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` + if test "X$output_objdir" = "X$output"; then + output_objdir="$objdir" + else + output_objdir="$output_objdir/$objdir" + fi + # Create the object directory. + if test ! -d "$output_objdir"; then + $show "$mkdir $output_objdir" + $run $mkdir $output_objdir + exit_status=$? + if test "$exit_status" -ne 0 && test ! -d "$output_objdir"; then + exit $exit_status + fi + fi + + # Determine the type of output + case $output in + "") + $echo "$modename: you must specify an output file" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + case $host in + *cygwin* | *mingw* | *pw32*) + # don't eliminate duplications in $postdeps and $predeps + duplicate_compiler_generated_deps=yes + ;; + *) + duplicate_compiler_generated_deps=$duplicate_deps + ;; + esac + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if test "X$duplicate_deps" = "Xyes" ; then + case "$libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + libs="$libs $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + esac + pre_post_deps="$pre_post_deps $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + case $linkmode in + lib) + passes="conv link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 + exit $EXIT_FAILURE + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + for pass in $passes; do + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; + esac + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + compiler_flags="$compiler_flags $deplib" + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 + continue + fi + name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` + for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if (${SED} -e '2q' $lib | + grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + library_names= + old_library= + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + *) + $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) lib="$deplib" ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method + match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + if eval $echo \"$deplib\" 2>/dev/null \ + | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + $echo + $echo "*** Warning: Trying to link with static lib archive $deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because the file extensions .$libext of this argument makes me believe" + $echo "*** that it is just a static archive that I should not used here." + else + $echo + $echo "*** Warning: Linking the shared library $output against the" + $echo "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + newdlprefiles="$newdlprefiles $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles="$newdlfiles $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + if test "$found" = yes || test -f "$lib"; then : + else + $echo "$modename: cannot find the library \`$lib' or unhandled argument \`$deplib'" 1>&2 + exit $EXIT_FAILURE + fi + + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles="$dlfiles $dlopen" + test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $ladir/$objdir/$old_library" + old_convenience="$old_convenience $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + $echo "$modename: \`$lib' is not a convenience library" 1>&2 + exit $EXIT_FAILURE + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + if test -z "$linklib"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + dlprefiles="$dlprefiles $lib $dependency_libs" + else + newdlfiles="$newdlfiles $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 + $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 + abs_ladir="$ladir" + fi + ;; + esac + laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + $echo "$modename: warning: library \`$lib' was moved." 1>&2 + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$libdir" + absdir="$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + fi + fi # $installed = yes + name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles="$newdlprefiles $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + newlib_search_path="$newlib_search_path $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath " in + *" $dir "*) ;; + *" $absdir "*) ;; + *) temp_rpath="$temp_rpath $absdir" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes ; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" + need_relink=yes + fi + # This is a shared library + + # Warn about portability, can't link against -module's on + # some systems (darwin) + if test "$shouldnotlink" = yes && test "$pass" = link ; then + $echo + if test "$linkmode" = prog; then + $echo "*** Warning: Linking the executable $output against the loadable module" + else + $echo "*** Warning: Linking the shared library $output against the loadable module" + fi + $echo "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + realname="$2" + shift; shift + libname=`eval \\$echo \"$libname_spec\"` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw*) + major=`expr $current - $age` + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + soname=`$echo $soroot | ${SED} -e 's/^.*\///'` + newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + $show "extracting exported symbol list from \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$extract_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + $show "generating import library for \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$old_archive_from_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a module then we can not link against + # it, someone is ignoring the new warnings I added + if /usr/bin/file -L $add 2> /dev/null | + $EGREP ": [^:]* bundle" >/dev/null ; then + $echo "** Warning, lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + $echo + $echo "** And there doesn't seem to be a static archive available" + $echo "** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$dir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + $echo "$modename: configuration error: unsupported hardcode properties" + exit $EXIT_FAILURE + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && \ + test "$hardcode_minus_L" != yes && \ + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + diff --git a/tests/examplefiles/matlab_noreturn b/tests/examplefiles/matlab_noreturn new file mode 100644 index 0000000..7802782 --- /dev/null +++ b/tests/examplefiles/matlab_noreturn @@ -0,0 +1,3 @@ + function myfunc(s) + a = 1; + end diff --git a/tests/examplefiles/matlab_sample b/tests/examplefiles/matlab_sample new file mode 100644 index 0000000..1834050 --- /dev/null +++ b/tests/examplefiles/matlab_sample @@ -0,0 +1,27 @@ +function zz=sample(aa) +%%%%%%%%%%%%%%%%%% +% some comments +%%%%%%%%%%%%%%%%%% + +x = 'a string'; % some 'ticks' in a comment +y = 'a string with ''interal'' quotes'; + +for i=1:20 + disp(i); +end + +a = rand(30); +b = rand(30); + +c = a .* b ./ a \ (b .* a + b - a); + +c = a' * b'; % note: these ticks are for transpose, not quotes. + +disp('a comment symbol, %, in a string'); + +!echo abc % this isn't a comment - it's passed to system command + +function y=myfunc(x) +y = exp(x); + + diff --git a/tests/examplefiles/matlabsession_sample.txt b/tests/examplefiles/matlabsession_sample.txt new file mode 100644 index 0000000..1b33c9c --- /dev/null +++ b/tests/examplefiles/matlabsession_sample.txt @@ -0,0 +1,37 @@ +>> +>> +>> a = 'okay' + +a = + +okay + +>> x = rand(3) % a matrix + +x = + + 0.8147 0.9134 0.2785 + 0.9058 0.6324 0.5469 + 0.1270 0.0975 0.9575 + +>> 1/0 + +ans = + + Inf + +>> foo +??? Undefined function or variable 'foo'. + +>> +>> +>> {cos(2*pi), 'testing'} + +ans = + + [1] 'testing' + +>> +>> +>> + diff --git a/tests/examplefiles/minimal.ns2 b/tests/examplefiles/minimal.ns2 new file mode 100644 index 0000000..e8a9269 --- /dev/null +++ b/tests/examplefiles/minimal.ns2 @@ -0,0 +1,4 @@ +class A = ( | a = self m. | ) ( + m = (^a isNil ifTrue: [0] ifFalse: [1]) +) +class B = C ( | b0 = 0. b1 = b0 + 1. | ) () diff --git a/tests/examplefiles/moin_SyntaxReference.txt b/tests/examplefiles/moin_SyntaxReference.txt new file mode 100644 index 0000000..a88fea4 --- /dev/null +++ b/tests/examplefiles/moin_SyntaxReference.txt @@ -0,0 +1,340 @@ +## Please edit system and help pages ONLY in the moinmaster wiki! For more +## information, please see MoinMaster:MoinPagesEditorGroup. +##master-page:Unknown-Page +##master-date:Unknown-Date +#acl MoinPagesEditorGroup:read,write,delete,revert All:read +#format wiki +#language en + +This page aims to introduce the most important elements of MoinMoin``'s syntax at a glance, showing first the markup verbatim and then how it is rendered by the wiki engine. Additionally, you'll find links to the relative help pages. Please note that some of the features depend on your configuration. + += Table of Contents = +{{{ +'''Contents''' (up to the 2nd level) +[[TableOfContents(2)]] +}}} +'''Contents''' (up to the 2nd level) +[[TableOfContents(2)]] + += Headings = +'''''see:''' HelpOnHeadlines'' +{{{ += heading 1st level = +== heading 2nd level == +=== heading 3rd level === +==== heading 4th level ==== +===== heading 5th level ===== +}}} += heading 1st level = +== heading 2nd level == +=== heading 3rd level === +==== heading 4th level ==== +===== heading 5th level ===== + += Text Formatting = +'''''see:''' HelpOnFormatting'' +{{{ + * ''emphasized (italics)'' + * '''boldface''' + * '''''bold italics''''' + * `monospace` + * {{{source code}}} + * __underline__ + * ,,sub,,script + * ^super^script + * ~-smaller-~ + * ~+larger+~ + * --(strike through)-- +}}} + * ''emphasized (italics)'' + * '''boldface''' + * '''''bold italics''''' + * `monospace` + * {{{source code}}} + * __underline__ + * ,,sub,,script + * ^super^script + * ~-smaller-~ + * ~+larger+~ + * --(strike through)-- + += Hyperlinks = +'''''see:''' HelpOnLinking'' +== Internal Links == +{{{ + * FrontPage + * ["FrontPage"] + * HelpOnEditing/SubPages + * /SubPage + * ../SiblingPage + * [:FrontPage:named link] + * [#anchorname] + * [#anchorname description] + * [wiki:Self:PageName#anchorname] + * [wiki:Self:PageName#anchorname description] + * attachment:filename.txt +}}} + * FrontPage + * ["FrontPage"] + * HelpOnEditing/SubPages + * /SubPage + * ../SiblingPage + * [:FrontPage:named link] + * [#anchorname] + * [#anchorname description] + * [wiki:Self:PageName#anchorname] + * [wiki:Self:PageName#anchorname description] + * attachment:filename.txt + +== External Links == +{{{ + * http://moinmoin.wikiwikiweb.de/ + * [http://moinmoin.wikiwikiweb.de/] + * [http://moinmoin.wikiwikiweb.de/ MoinMoin Wiki] + * [http://moinmoin.wikiwikiweb.de/wiki/moinmoin.png] + * http://moinmoin.wikiwikiweb.de/wiki/moinmoin.png + * [http://moinmoin.wikiwikiweb.de/wiki/moinmoin.png moinmoin.png] + * MeatBall:InterWiki + * wiki:MeatBall/InterWiki + * [wiki:MeatBall/InterWiki] + * [wiki:MeatBall/InterWiki InterWiki page on MeatBall] + * [file://///servername/share/full/path/to/file/filename%20with%20spaces.txt link to file filename with spaces.txt] + * user@example.com +}}} + * http://moinmoin.wikiwikiweb.de/ + * [http://moinmoin.wikiwikiweb.de/] + * [http://moinmoin.wikiwikiweb.de/ MoinMoin Wiki] + * [http://moinmoin.wikiwikiweb.de/wiki/moinmoin.png] + * http://moinmoin.wikiwikiweb.de/wiki/moinmoin.png + * [http://moinmoin.wikiwikiweb.de/wiki/moinmoin.png moinmoin.png] + * MeatBall:InterWiki + * wiki:MeatBall/InterWiki + * [wiki:MeatBall/InterWiki] + * [wiki:MeatBall/InterWiki InterWiki page on MeatBall] + * [file://///servername/share/full/path/to/file/filename%20with%20spaces.txt link to file filename with spaces.txt] + * user@example.com + +== Avoid or Limit Automatical Linking == +{{{ + * Wiki''''''Name + * Wiki``Name + * !WikiName + * WikiName''''''s + * WikiName``s + * `http://www.example.com` +}}} + * Wiki''''''Name + * Wiki``Name + * !WikiName + * WikiName''''''s + * WikiName``s + * `http://www.example.com` + += Blockquotes and Indentions = +{{{ + indented text + text indented to the 2nd level +}}} + indented text + text indented to the 2nd level + += Lists = +'''''see:''' HelpOnLists'' +== Unordered Lists == +{{{ + * item 1 + + * item 2 (preceding white space) + * item 2.1 + * item 2.1.1 + * item 3 + . item 3.1 (bulletless) + . item 4 (bulletless) + * item 4.1 + . item 4.1.1 (bulletless) +}}} + * item 1 + + * item 2 (preceding white space) + * item 2.1 + * item 2.1.1 + * item 3 + . item 3.1 (bulletless) + . item 4 (bulletless) + * item 4.1 + . item 4.1.1 (bulletless) + +== Ordered Lists == +=== with Numbers === +{{{ + 1. item 1 + 1. item 1.1 + 1. item 1.2 + 1. item 2 +}}} + 1. item 1 + 1. item 1.1 + 1. item 1.2 + 1. item 2 + +=== with Roman Numbers === +{{{ + I. item 1 + i. item 1.1 + i. item 1.2 + I. item 2 +}}} + I. item 1 + i. item 1.1 + i. item 1.2 + I. item 2 + +=== with Letters === +{{{ + A. item A + a. item A. a) + a. item A. b) + A. item B +}}} + A. item A + a. item A. a) + a. item A. b) + A. item B + +== Definition Lists == +{{{ + term:: definition + object:: description 1 + :: description 2 + Action Items:: + :: First Item + :: Second Item +}}} + term:: definition + object:: description 1 + :: description 2 + Action Items:: + :: First Item + :: Second Item + += Horizontal Rules = +'''''see:''' HelpOnRules'' +{{{ +---- +----- +------ +------- +-------- +--------- +---------- +}}} +---- +----- +------ +------- +-------- +--------- +---------- + += Tables = +'''''see:''' HelpOnTables'' +== Tables == +{{{ +||'''A'''||'''B'''||'''C'''|| +||1 ||2 ||3 || +}}} +||'''A'''||'''B'''||'''C'''|| +||1 ||2 ||3 || + +== Cell Width == +{{{ +||minimal width ||<99%>maximal width || +}}} +||minimal width ||<99%>maximal width || + +== Spanning Rows and Columns == +{{{ +||<|2> cell spanning 2 rows ||cell in the 2nd column || +||cell in the 2nd column of the 2nd row || +||<-2> cell spanning 2 columns || +||||use empty cells as a shorthand || +}}} +||<|2> cell spanning 2 rows ||cell in the 2nd column || +||cell in the 2nd column of the 2nd row || +||<-2> cell spanning 2 columns || +||||use empty cells as a shorthand || + +== Alignment of Cell Contents == +{{{ +||<^|3> top (combined) ||<:99%> center (combined) || bottom (combined) || +||<)> right || +||<(> left || +}}} +||<^|3> top (combined) ||<:99%> center (combined) || bottom (combined) || +||<)> right || +||<(> left || + +== Coulored Table Cells == +{{{ +||<#0000FF> blue ||<#00FF00> green ||<#FF0000> red || +||<#00FFFF> cyan ||<#FF00FF> magenta ||<#FFFF00> yellow || +}}} +||<#0000FF> blue ||<#00FF00> green ||<#FF0000> red || +||<#00FFFF> cyan ||<#FF00FF> magenta ||<#FFFF00> yellow || + +== HTML-like Options for Tables == +{{{ +||A || like <|2> || +|| like <#00FF00> || +|| like <-2>|| +}}} +||A || like <|2> || +|| like <#00FF00> || +|| like <-2>|| + += Macros and Variables = +== Macros == +'''''see:''' HelpOnMacros'' + * `[[Anchor(anchorname)]]` inserts a link anchor `anchorname` + * `[[BR]]` inserts a hard line break + * `[[FootNote(Note)]]` inserts a footnote saying `Note` + * `[[Include(HelpOnMacros/Include)]]` inserts the contents of the page `HelpOnMacros/Include` inline + * `[[MailTo(user AT example DOT com)]]` obfuscates the email address `user@example.com` to users not logged in + +== Variables == +'''''see:''' HelpOnVariables'' + * `@``SIG``@` inserts your login name and timestamp of modification + * `@``TIME``@` inserts date and time of modification + += Smileys and Icons = +'''''see:''' HelpOnSmileys'' +[[ShowSmileys]] + += Source code = +'''''see:''' HelpOnParsers'' +== Verbatim Display == +{{{ +{ { { +def hello(): + print "Hello World!" +} } } +}}} +/!\ Remove spaces between "`{ { {`" and "`} } }`". +{{{ +def hello(): + print "Hello World!" +}}} + +== Syntax Highlighting == +{{{ +{ { {#!python +def hello(): + print "Hello World!" +} } } +}}} +/!\ Remove spaces between "`{ { {`" and "`} } }`". +{{{#!python +def hello(): + print "Hello World!" +}}} + diff --git a/tests/examplefiles/multiline_regexes.rb b/tests/examplefiles/multiline_regexes.rb new file mode 100644 index 0000000..1b1e761 --- /dev/null +++ b/tests/examplefiles/multiline_regexes.rb @@ -0,0 +1,38 @@ +/ +this is a +multiline +regex +/ + +this /is a +multiline regex too/ + +foo = /is also +one/ + +also /4 +is one/ + +this(/ +too +/) + +# this not +2 /4 +asfsadf/ + +# this is also not one +0x4d /25 +foo/ + +42 and /this +is also a multiline +regex/ + + +# And here some special string cases +foo = % blah # comment here to ensure whitespace +foo(% blah ) +foo << % blah # stupid but has to work +foo = % blah + % blub # wicked +foo = %q wicked # works too diff --git a/tests/examplefiles/nasm_aoutso.asm b/tests/examplefiles/nasm_aoutso.asm new file mode 100644 index 0000000..9fd9727 --- /dev/null +++ b/tests/examplefiles/nasm_aoutso.asm @@ -0,0 +1,96 @@ +; test source file for assembling to NetBSD/FreeBSD a.out shared library +; build with: +; nasm -f aoutb aoutso.asm +; ld -Bshareable -o aoutso.so aoutso.o +; test with: +; cc -o aoutso aouttest.c aoutso.so +; ./aoutso + +; This file should test the following: +; [1] Define and export a global text-section symbol +; [2] Define and export a global data-section symbol +; [3] Define and export a global BSS-section symbol +; [4] Define a non-global text-section symbol +; [5] Define a non-global data-section symbol +; [6] Define a non-global BSS-section symbol +; [7] Define a COMMON symbol +; [8] Define a NASM local label +; [9] Reference a NASM local label +; [10] Import an external symbol +; [11] Make a PC-relative call to an external symbol +; [12] Reference a text-section symbol in the text section +; [13] Reference a data-section symbol in the text section +; [14] Reference a BSS-section symbol in the text section +; [15] Reference a text-section symbol in the data section +; [16] Reference a data-section symbol in the data section +; [17] Reference a BSS-section symbol in the data section + + BITS 32 + EXTERN __GLOBAL_OFFSET_TABLE_ + GLOBAL _lrotate:function ; [1] + GLOBAL _greet:function ; [1] + GLOBAL _asmstr:data _asmstr.end-_asmstr ; [2] + GLOBAL _textptr:data 4 ; [2] + GLOBAL _selfptr:data 4 ; [2] + GLOBAL _integer:data 4 ; [3] + EXTERN _printf ; [10] + COMMON _commvar 4 ; [7] + + SECTION .text + +; prototype: long lrotate(long x, int num); +_lrotate: ; [1] + push ebp + mov ebp,esp + mov eax,[ebp+8] + mov ecx,[ebp+12] +.label rol eax,1 ; [4] [8] + loop .label ; [9] [12] + mov esp,ebp + pop ebp + ret + +; prototype: void greet(void); +_greet push ebx ; we'll use EBX for GOT, so save it + call .getgot +.getgot: pop ebx + add ebx,__GLOBAL_OFFSET_TABLE_ + $$ - .getgot wrt ..gotpc + mov eax,[ebx+_integer wrt ..got] ; [14] + mov eax,[eax] + inc eax + mov [ebx+localint wrt ..gotoff],eax ; [14] + mov eax,[ebx+_commvar wrt ..got] + push dword [eax] + mov eax,[ebx+localptr wrt ..gotoff] ; [13] + push dword [eax] + mov eax,[ebx+_integer wrt ..got] ; [1] [14] + push dword [eax] + lea eax,[ebx+_printfstr wrt ..gotoff] + push eax ; [13] + call _printf wrt ..plt ; [11] + add esp,16 + pop ebx + ret + + SECTION .data + +; a string +_asmstr db 'hello, world', 0 ; [2] +.end + +; a string for Printf +_printfstr db "integer==%d, localint==%d, commvar=%d" + db 10, 0 + +; some pointers +localptr dd localint ; [5] [17] +_textptr dd _greet wrt ..sym ; [15] +_selfptr dd _selfptr wrt ..sym ; [16] + + SECTION .bss + +; an integer +_integer resd 1 ; [3] + +; a local integer +localint resd 1 ; [6] diff --git a/tests/examplefiles/nasm_objexe.asm b/tests/examplefiles/nasm_objexe.asm new file mode 100644 index 0000000..dcae5ee --- /dev/null +++ b/tests/examplefiles/nasm_objexe.asm @@ -0,0 +1,30 @@ +; Demonstration of how to write an entire .EXE format program as a .OBJ +; file to be linked. Tested with the VAL free linker. +; To build: +; nasm -fobj objexe.asm +; val objexe.obj,objexe.exe; +; To test: +; objexe +; (should print `hello, world') + + segment code + +..start: mov ax,data + mov ds,ax + mov ax,stack + mov ss,ax + mov sp,stacktop + + mov dx,hello + mov ah,9 + int 0x21 + + mov ax,0x4c00 + int 0x21 + + segment data +hello: db 'hello, world', 13, 10, '$' + + segment stack stack + resb 64 +stacktop: diff --git a/tests/examplefiles/nginx_nginx.conf b/tests/examplefiles/nginx_nginx.conf new file mode 100644 index 0000000..9dcdc8a --- /dev/null +++ b/tests/examplefiles/nginx_nginx.conf @@ -0,0 +1,118 @@ + +#user nobody; +worker_processes 1; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +#pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] $request ' + '"$status" $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + #access_log logs/access.log main; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #gzip on; + + server { + listen 80; + server_name localhost; + + charset koi8-r; + + #access_log logs/host.access.log main; + + location / { + root html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + location ~ \.php$ { + proxy_pass http://127.0.0.1; + } + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + location ~ \.php$ { + root html; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; + include fastcgi_params; + } + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + location ~ /\.ht { + deny all; + } + } + + + # another virtual host using mix of IP-, name-, and port-based configuration + # + server { + listen 8000; + listen somename:8080; + server_name somename alias another.alias; + + location / { + root html; + index index.html index.htm; + } + } + + + # HTTPS server + # + server { + listen 443; + server_name localhost; + + ssl on; + ssl_certificate cert.pem; + ssl_certificate_key cert.key; + + ssl_session_timeout 5m; + + ssl_protocols SSLv2 SSLv3 TLSv1; + ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; + ssl_prefer_server_ciphers on; + + location / { + root html; + index index.html index.htm; + } + } + +} diff --git a/tests/examplefiles/numbers.c b/tests/examplefiles/numbers.c new file mode 100644 index 0000000..80662ea --- /dev/null +++ b/tests/examplefiles/numbers.c @@ -0,0 +1,12 @@ +/* + * Some Number Test + */ + +int i = 24241424; +float f1 = 342423423.24234; +float f2 = 25235235.; +float f3 = .234234; +float f4 = 234243e+34343; +float f5 = 24234e-234; +int o = 0234; +int h = 0x2342; diff --git a/tests/examplefiles/objc_example.m b/tests/examplefiles/objc_example.m new file mode 100644 index 0000000..c2a1c41 --- /dev/null +++ b/tests/examplefiles/objc_example.m @@ -0,0 +1,11 @@ +#import "Somefile.h" + +NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys: + @"quattuor", @"four", @"quinque", @"five", @"sex", @"six", nil]; + + +NSString *key; +for (key in dictionary) { + NSLog(@"English: %@, Latin: %@", key, [dictionary valueForKey:key]); +} + diff --git a/tests/examplefiles/objc_example2.m b/tests/examplefiles/objc_example2.m new file mode 100644 index 0000000..8cd9b06 --- /dev/null +++ b/tests/examplefiles/objc_example2.m @@ -0,0 +1,24 @@ +// MyClass.h +@interface MyClass : NSObject +{ + NSString *value; + NSTextField *textField; +@private + NSDate *lastModifiedDate; +} +@property(copy, readwrite) NSString *value; +@property(retain) IBOutlet NSTextField *textField; +@end + +// MyClass.m +// Class extension to declare private property +@interface MyClass () +@property(retain) NSDate *lastModifiedDate; +@end + +@implementation MyClass +@synthesize value; +@synthesize textField; +@synthesize lastModifiedDate; +// implementation continues +@end diff --git a/tests/examplefiles/perl_perl5db b/tests/examplefiles/perl_perl5db new file mode 100644 index 0000000..ab9d5e3 --- /dev/null +++ b/tests/examplefiles/perl_perl5db @@ -0,0 +1,998 @@ + +=head1 NAME + +perl5db.pl - the perl debugger + +=head1 SYNOPSIS + + perl -d your_Perl_script + +=head1 DESCRIPTION + +After this routine is over, we don't have user code executing in the debugger's +context, so we can use C freely. + +=cut + +############################################## Begin lexical danger zone + +# 'my' variables used here could leak into (that is, be visible in) +# the context that the code being evaluated is executing in. This means that +# the code could modify the debugger's variables. +# +# Fiddling with the debugger's context could be Bad. We insulate things as +# much as we can. + +sub eval { + + # 'my' would make it visible from user code + # but so does local! --tchrist + # Remember: this localizes @DB::res, not @main::res. + local @res; + { + + # Try to keep the user code from messing with us. Save these so that + # even if the eval'ed code changes them, we can put them back again. + # Needed because the user could refer directly to the debugger's + # package globals (and any 'my' variables in this containing scope) + # inside the eval(), and we want to try to stay safe. + local $otrace = $trace; + local $osingle = $single; + local $od = $^D; + + # Untaint the incoming eval() argument. + { ($evalarg) = $evalarg =~ /(.*)/s; } + + # $usercontext built in DB::DB near the comment + # "set up the context for DB::eval ..." + # Evaluate and save any results. + @res = eval "$usercontext $evalarg;\n"; # '\n' for nice recursive debug + + # Restore those old values. + $trace = $otrace; + $single = $osingle; + $^D = $od; + } + + # Save the current value of $@, and preserve it in the debugger's copy + # of the saved precious globals. + my $at = $@; + + # Since we're only saving $@, we only have to localize the array element + # that it will be stored in. + local $saved[0]; # Preserve the old value of $@ + eval { &DB::save }; + + # Now see whether we need to report an error back to the user. + if ($at) { + local $\ = ''; + print $OUT $at; + } + + # Display as required by the caller. $onetimeDump and $onetimedumpDepth + # are package globals. + elsif ($onetimeDump) { + if ( $onetimeDump eq 'dump' ) { + local $option{dumpDepth} = $onetimedumpDepth + if defined $onetimedumpDepth; + dumpit( $OUT, \@res ); + } + elsif ( $onetimeDump eq 'methods' ) { + methods( $res[0] ); + } + } ## end elsif ($onetimeDump) + @res; +} ## end sub eval + +############################################## End lexical danger zone + +# After this point it is safe to introduce lexicals. +# The code being debugged will be executing in its own context, and +# can't see the inside of the debugger. +# +# However, one should not overdo it: leave as much control from outside as +# possible. If you make something a lexical, it's not going to be addressable +# from outside the debugger even if you know its name. + +# This file is automatically included if you do perl -d. +# It's probably not useful to include this yourself. +# +# Before venturing further into these twisty passages, it is +# wise to read the perldebguts man page or risk the ire of dragons. +# +# (It should be noted that perldebguts will tell you a lot about +# the underlying mechanics of how the debugger interfaces into the +# Perl interpreter, but not a lot about the debugger itself. The new +# comments in this code try to address this problem.) + +# Note that no subroutine call is possible until &DB::sub is defined +# (for subroutines defined outside of the package DB). In fact the same is +# true if $deep is not defined. + +# Enhanced by ilya@math.ohio-state.edu (Ilya Zakharevich) + +# modified Perl debugger, to be run from Emacs in perldb-mode +# Ray Lischner (uunet!mntgfx!lisch) as of 5 Nov 1990 +# Johan Vromans -- upgrade to 4.0 pl 10 +# Ilya Zakharevich -- patches after 5.001 (and some before ;-) + +# (We have made efforts to clarify the comments in the change log +# in other places; some of them may seem somewhat obscure as they +# were originally written, and explaining them away from the code +# in question seems conterproductive.. -JM) + +=head1 DEBUGGER INITIALIZATION + +The debugger starts up in phases. + +=head2 BASIC SETUP + +First, it initializes the environment it wants to run in: turning off +warnings during its own compilation, defining variables which it will need +to avoid warnings later, setting itself up to not exit when the program +terminates, and defaulting to printing return values for the C command. + +=cut + +# Needed for the statement after exec(): +# +# This BEGIN block is simply used to switch off warnings during debugger +# compiliation. Probably it would be better practice to fix the warnings, +# but this is how it's done at the moment. + +BEGIN { + $ini_warn = $^W; + $^W = 0; +} # Switch compilation warnings off until another BEGIN. + +# test if assertions are supported and actived: +BEGIN { + $ini_assertion = eval "sub asserting_test : assertion {1}; 1"; + + # $ini_assertion = undef => assertions unsupported, + # " = 1 => assertions supported + # print "\$ini_assertion=$ini_assertion\n"; +} + +local ($^W) = 0; # Switch run-time warnings off during init. + +=head2 THREADS SUPPORT + +If we are running under a threaded Perl, we require threads and threads::shared +if the environment variable C is set, to enable proper +threaded debugger control. C<-dt> can also be used to set this. + +Each new thread will be announced and the debugger prompt will always inform +you of each new thread created. It will also indicate the thread id in which +we are currently running within the prompt like this: + + [tid] DB<$i> + +Where C<[tid]> is an integer thread id and C<$i> is the familiar debugger +command prompt. The prompt will show: C<[0]> when running under threads, but +not actually in a thread. C<[tid]> is consistent with C usage. + +While running under threads, when you set or delete a breakpoint (etc.), this +will apply to all threads, not just the currently running one. When you are +in a currently executing thread, you will stay there until it completes. With +the current implementation it is not currently possible to hop from one thread +to another. + +The C and C commands are currently fairly minimal - see C and C. + +Note that threading support was built into the debugger as of Perl version +C<5.8.6> and debugger version C<1.2.8>. + +=cut + +BEGIN { + # ensure we can share our non-threaded variables or no-op + if ($ENV{PERL5DB_THREADED}) { + require threads; + require threads::shared; + import threads::shared qw(share); + $DBGR; + share(\$DBGR); + lock($DBGR); + print "Threads support enabled\n"; + } else { + *lock = sub(*) {}; + *share = sub(*) {}; + } +} + +# This would probably be better done with "use vars", but that wasn't around +# when this code was originally written. (Neither was "use strict".) And on +# the principle of not fiddling with something that was working, this was +# left alone. +warn( # Do not ;-) + # These variables control the execution of 'dumpvar.pl'. + $dumpvar::hashDepth, + $dumpvar::arrayDepth, + $dumpvar::dumpDBFiles, + $dumpvar::dumpPackages, + $dumpvar::quoteHighBit, + $dumpvar::printUndef, + $dumpvar::globPrint, + $dumpvar::usageOnly, + + # used to save @ARGV and extract any debugger-related flags. + @ARGS, + + # used to control die() reporting in diesignal() + $Carp::CarpLevel, + + # used to prevent multiple entries to diesignal() + # (if for instance diesignal() itself dies) + $panic, + + # used to prevent the debugger from running nonstop + # after a restart + $second_time, + ) + if 0; + +foreach my $k (keys (%INC)) { + &share(\$main::{'_<'.$filename}); +}; + +# Command-line + PERLLIB: +# Save the contents of @INC before they are modified elsewhere. +@ini_INC = @INC; + +# This was an attempt to clear out the previous values of various +# trapped errors. Apparently it didn't help. XXX More info needed! +# $prevwarn = $prevdie = $prevbus = $prevsegv = ''; # Does not help?! + +# We set these variables to safe values. We don't want to blindly turn +# off warnings, because other packages may still want them. +$trace = $signal = $single = 0; # Uninitialized warning suppression + # (local $^W cannot help - other packages!). + +# Default to not exiting when program finishes; print the return +# value when the 'r' command is used to return from a subroutine. +$inhibit_exit = $option{PrintRet} = 1; + +=head1 OPTION PROCESSING + +The debugger's options are actually spread out over the debugger itself and +C; some of these are variables to be set, while others are +subs to be called with a value. To try to make this a little easier to +manage, the debugger uses a few data structures to define what options +are legal and how they are to be processed. + +First, the C<@options> array defines the I of all the options that +are to be accepted. + +=cut + +@options = qw( + CommandSet + hashDepth arrayDepth dumpDepth + DumpDBFiles DumpPackages DumpReused + compactDump veryCompact quote + HighBit undefPrint globPrint + PrintRet UsageOnly frame + AutoTrace TTY noTTY + ReadLine NonStop LineInfo + maxTraceLen recallCommand ShellBang + pager tkRunning ornaments + signalLevel warnLevel dieLevel + inhibit_exit ImmediateStop bareStringify + CreateTTY RemotePort windowSize + DollarCaretP OnlyAssertions WarnAssertions +); + +@RememberOnROptions = qw(DollarCaretP OnlyAssertions); + +=pod + +Second, C lists the variables that each option uses to save its +state. + +=cut + +%optionVars = ( + hashDepth => \$dumpvar::hashDepth, + arrayDepth => \$dumpvar::arrayDepth, + CommandSet => \$CommandSet, + DumpDBFiles => \$dumpvar::dumpDBFiles, + DumpPackages => \$dumpvar::dumpPackages, + DumpReused => \$dumpvar::dumpReused, + HighBit => \$dumpvar::quoteHighBit, + undefPrint => \$dumpvar::printUndef, + globPrint => \$dumpvar::globPrint, + UsageOnly => \$dumpvar::usageOnly, + CreateTTY => \$CreateTTY, + bareStringify => \$dumpvar::bareStringify, + frame => \$frame, + AutoTrace => \$trace, + inhibit_exit => \$inhibit_exit, + maxTraceLen => \$maxtrace, + ImmediateStop => \$ImmediateStop, + RemotePort => \$remoteport, + windowSize => \$window, + WarnAssertions => \$warnassertions, +); + +=pod + +Third, C<%optionAction> defines the subroutine to be called to process each +option. + +=cut + +%optionAction = ( + compactDump => \&dumpvar::compactDump, + veryCompact => \&dumpvar::veryCompact, + quote => \&dumpvar::quote, + TTY => \&TTY, + noTTY => \&noTTY, + ReadLine => \&ReadLine, + NonStop => \&NonStop, + LineInfo => \&LineInfo, + recallCommand => \&recallCommand, + ShellBang => \&shellBang, + pager => \&pager, + signalLevel => \&signalLevel, + warnLevel => \&warnLevel, + dieLevel => \&dieLevel, + tkRunning => \&tkRunning, + ornaments => \&ornaments, + RemotePort => \&RemotePort, + DollarCaretP => \&DollarCaretP, + OnlyAssertions=> \&OnlyAssertions, +); + +=pod + +Last, the C<%optionRequire> notes modules that must be Cd if an +option is used. + +=cut + +# Note that this list is not complete: several options not listed here +# actually require that dumpvar.pl be loaded for them to work, but are +# not in the table. A subsequent patch will correct this problem; for +# the moment, we're just recommenting, and we are NOT going to change +# function. +%optionRequire = ( + compactDump => 'dumpvar.pl', + veryCompact => 'dumpvar.pl', + quote => 'dumpvar.pl', +); + +=pod + +There are a number of initialization-related variables which can be set +by putting code to set them in a BEGIN block in the C environment +variable. These are: + +=over 4 + +=item C<$rl> - readline control XXX needs more explanation + +=item C<$warnLevel> - whether or not debugger takes over warning handling + +=item C<$dieLevel> - whether or not debugger takes over die handling + +=item C<$signalLevel> - whether or not debugger takes over signal handling + +=item C<$pre> - preprompt actions (array reference) + +=item C<$post> - postprompt actions (array reference) + +=item C<$pretype> + +=item C<$CreateTTY> - whether or not to create a new TTY for this debugger + +=item C<$CommandSet> - which command set to use (defaults to new, documented set) + +=back + +=cut + +# These guys may be defined in $ENV{PERL5DB} : +$rl = 1 unless defined $rl; +$warnLevel = 1 unless defined $warnLevel; +$dieLevel = 1 unless defined $dieLevel; +$signalLevel = 1 unless defined $signalLevel; +$pre = [] unless defined $pre; +$post = [] unless defined $post; +$pretype = [] unless defined $pretype; +$CreateTTY = 3 unless defined $CreateTTY; +$CommandSet = '580' unless defined $CommandSet; + +share($rl); +share($warnLevel); +share($dieLevel); +share($signalLevel); +share($pre); +share($post); +share($pretype); +share($rl); +share($CreateTTY); +share($CommandSet); + +=pod + +The default C, C, and C handlers are set up. + +=cut + +warnLevel($warnLevel); +dieLevel($dieLevel); +signalLevel($signalLevel); + +=pod + +The pager to be used is needed next. We try to get it from the +environment first. if it's not defined there, we try to find it in +the Perl C. If it's not there, we default to C. We +then call the C function to save the pager name. + +=cut + +# This routine makes sure $pager is set up so that '|' can use it. +pager( + + # If PAGER is defined in the environment, use it. + defined $ENV{PAGER} + ? $ENV{PAGER} + + # If not, see if Config.pm defines it. + : eval { require Config } + && defined $Config::Config{pager} + ? $Config::Config{pager} + + # If not, fall back to 'more'. + : 'more' + ) + unless defined $pager; + +=pod + +We set up the command to be used to access the man pages, the command +recall character (C unless otherwise defined) and the shell escape +character (C unless otherwise defined). Yes, these do conflict, and +neither works in the debugger at the moment. + +=cut + +setman(); + +# Set up defaults for command recall and shell escape (note: +# these currently don't work in linemode debugging). +&recallCommand("!") unless defined $prc; +&shellBang("!") unless defined $psh; + +=pod + +We then set up the gigantic string containing the debugger help. +We also set the limit on the number of arguments we'll display during a +trace. + +=cut + +sethelp(); + +# If we didn't get a default for the length of eval/stack trace args, +# set it here. +$maxtrace = 400 unless defined $maxtrace; + +=head2 SETTING UP THE DEBUGGER GREETING + +The debugger I helps to inform the user how many debuggers are +running, and whether the current debugger is the primary or a child. + +If we are the primary, we just hang onto our pid so we'll have it when +or if we start a child debugger. If we are a child, we'll set things up +so we'll have a unique greeting and so the parent will give us our own +TTY later. + +We save the current contents of the C environment variable +because we mess around with it. We'll also need to hang onto it because +we'll need it if we restart. + +Child debuggers make a label out of the current PID structure recorded in +PERLDB_PIDS plus the new PID. They also mark themselves as not having a TTY +yet so the parent will give them one later via C. + +=cut + +# Save the current contents of the environment; we're about to +# much with it. We'll need this if we have to restart. +$ini_pids = $ENV{PERLDB_PIDS}; + +if ( defined $ENV{PERLDB_PIDS} ) { + + # We're a child. Make us a label out of the current PID structure + # recorded in PERLDB_PIDS plus our (new) PID. Mark us as not having + # a term yet so the parent will give us one later via resetterm(). + $pids = "[$ENV{PERLDB_PIDS}]"; + $ENV{PERLDB_PIDS} .= "->$$"; + $term_pid = -1; +} ## end if (defined $ENV{PERLDB_PIDS... +else { + + # We're the parent PID. Initialize PERLDB_PID in case we end up with a + # child debugger, and mark us as the parent, so we'll know to set up + # more TTY's is we have to. + $ENV{PERLDB_PIDS} = "$$"; + $pids = "{pid=$$}"; + $term_pid = $$; +} + +$pidprompt = ''; + +# Sets up $emacs as a synonym for $slave_editor. +*emacs = $slave_editor if $slave_editor; # May be used in afterinit()... + +=head2 READING THE RC FILE + +The debugger will read a file of initialization options if supplied. If +running interactively, this is C<.perldb>; if not, it's C. + +=cut + +# As noted, this test really doesn't check accurately that the debugger +# is running at a terminal or not. + +if ( -e "/dev/tty" ) { # this is the wrong metric! + $rcfile = ".perldb"; +} +else { + $rcfile = "perldb.ini"; +} + +=pod + +The debugger does a safety test of the file to be read. It must be owned +either by the current user or root, and must only be writable by the owner. + +=cut + +# This wraps a safety test around "do" to read and evaluate the init file. +# +# This isn't really safe, because there's a race +# between checking and opening. The solution is to +# open and fstat the handle, but then you have to read and +# eval the contents. But then the silly thing gets +# your lexical scope, which is unfortunate at best. +sub safe_do { + my $file = shift; + + # Just exactly what part of the word "CORE::" don't you understand? + local $SIG{__WARN__}; + local $SIG{__DIE__}; + + unless ( is_safe_file($file) ) { + CORE::warn < command is invoked, it +tries to capture all of the state it can into environment variables, and +then sets C. When we start executing again, we check to see +if C is there; if so, we reload all the information that +the R command stuffed into the environment variables. + + PERLDB_RESTART - flag only, contains no restart data itself. + PERLDB_HIST - command history, if it's available + PERLDB_ON_LOAD - breakpoints set by the rc file + PERLDB_POSTPONE - subs that have been loaded/not executed, and have actions + PERLDB_VISITED - files that had breakpoints + PERLDB_FILE_... - breakpoints for a file + PERLDB_OPT - active options + PERLDB_INC - the original @INC + PERLDB_PRETYPE - preprompt debugger actions + PERLDB_PRE - preprompt Perl code + PERLDB_POST - post-prompt Perl code + PERLDB_TYPEAHEAD - typeahead captured by readline() + +We chug through all these variables and plug the values saved in them +back into the appropriate spots in the debugger. + +=cut + +if ( exists $ENV{PERLDB_RESTART} ) { + + # We're restarting, so we don't need the flag that says to restart anymore. + delete $ENV{PERLDB_RESTART}; + + # $restart = 1; + @hist = get_list('PERLDB_HIST'); + %break_on_load = get_list("PERLDB_ON_LOAD"); + %postponed = get_list("PERLDB_POSTPONE"); + + share(@hist); + share(@truehist); + share(%break_on_load); + share(%postponed); + + # restore breakpoints/actions + my @had_breakpoints = get_list("PERLDB_VISITED"); + for ( 0 .. $#had_breakpoints ) { + my %pf = get_list("PERLDB_FILE_$_"); + $postponed_file{ $had_breakpoints[$_] } = \%pf if %pf; + } + + # restore options + my %opt = get_list("PERLDB_OPT"); + my ( $opt, $val ); + while ( ( $opt, $val ) = each %opt ) { + $val =~ s/[\\\']/\\$1/g; + parse_options("$opt'$val'"); + } + + # restore original @INC + @INC = get_list("PERLDB_INC"); + @ini_INC = @INC; + + # return pre/postprompt actions and typeahead buffer + $pretype = [ get_list("PERLDB_PRETYPE") ]; + $pre = [ get_list("PERLDB_PRE") ]; + $post = [ get_list("PERLDB_POST") ]; + @typeahead = get_list( "PERLDB_TYPEAHEAD", @typeahead ); +} ## end if (exists $ENV{PERLDB_RESTART... + +=head2 SETTING UP THE TERMINAL + +Now, we'll decide how the debugger is going to interact with the user. +If there's no TTY, we set the debugger to run non-stop; there's not going +to be anyone there to enter commands. + +=cut + +if ($notty) { + $runnonstop = 1; + share($runnonstop); +} + +=pod + +If there is a TTY, we have to determine who it belongs to before we can +proceed. If this is a slave editor or graphical debugger (denoted by +the first command-line switch being '-emacs'), we shift this off and +set C<$rl> to 0 (XXX ostensibly to do straight reads). + +=cut + +else { + + # Is Perl being run from a slave editor or graphical debugger? + # If so, don't use readline, and set $slave_editor = 1. + $slave_editor = + ( ( defined $main::ARGV[0] ) and ( $main::ARGV[0] eq '-emacs' ) ); + $rl = 0, shift(@main::ARGV) if $slave_editor; + + #require Term::ReadLine; + +=pod + +We then determine what the console should be on various systems: + +=over 4 + +=item * Cygwin - We use C instead of a separate device. + +=cut + + if ( $^O eq 'cygwin' ) { + + # /dev/tty is binary. use stdin for textmode + undef $console; + } + +=item * Unix - use C. + +=cut + + elsif ( -e "/dev/tty" ) { + $console = "/dev/tty"; + } + +=item * Windows or MSDOS - use C. + +=cut + + elsif ( $^O eq 'dos' or -e "con" or $^O eq 'MSWin32' ) { + $console = "con"; + } + +=item * MacOS - use C if this is the MPW version; C if not. + +Note that Mac OS X returns C, not C. Also note that the debugger doesn't do anything special for C. Maybe it should. + +=cut + + elsif ( $^O eq 'MacOS' ) { + if ( $MacPerl::Version !~ /MPW/ ) { + $console = + "Dev:Console:Perl Debug"; # Separate window for application + } + else { + $console = "Dev:Console"; + } + } ## end elsif ($^O eq 'MacOS') + +=item * VMS - use C. + +=cut + + else { + + # everything else is ... + $console = "sys\$command"; + } + +=pod + +=back + +Several other systems don't use a specific console. We C +for those (Windows using a slave editor/graphical debugger, NetWare, OS/2 +with a slave editor, Epoc). + +=cut + + if ( ( $^O eq 'MSWin32' ) and ( $slave_editor or defined $ENV{EMACS} ) ) { + + # /dev/tty is binary. use stdin for textmode + $console = undef; + } + + if ( $^O eq 'NetWare' ) { + + # /dev/tty is binary. use stdin for textmode + $console = undef; + } + + # In OS/2, we need to use STDIN to get textmode too, even though + # it pretty much looks like Unix otherwise. + if ( defined $ENV{OS2_SHELL} and ( $slave_editor or $ENV{WINDOWID} ) ) + { # In OS/2 + $console = undef; + } + + # EPOC also falls into the 'got to use STDIN' camp. + if ( $^O eq 'epoc' ) { + $console = undef; + } + +=pod + +If there is a TTY hanging around from a parent, we use that as the console. + +=cut + + $console = $tty if defined $tty; + +=head2 SOCKET HANDLING + +The debugger is capable of opening a socket and carrying out a debugging +session over the socket. + +If C was defined in the options, the debugger assumes that it +should try to start a debugging session on that port. It builds the socket +and then tries to connect the input and output filehandles to it. + +=cut + + # Handle socket stuff. + + if ( defined $remoteport ) { + + # If RemotePort was defined in the options, connect input and output + # to the socket. + require IO::Socket; + $OUT = new IO::Socket::INET( + Timeout => '10', + PeerAddr => $remoteport, + Proto => 'tcp', + ); + if ( !$OUT ) { die "Unable to connect to remote host: $remoteport\n"; } + $IN = $OUT; + } ## end if (defined $remoteport) + +=pod + +If no C was defined, and we want to create a TTY on startup, +this is probably a situation where multiple debuggers are running (for example, +a backticked command that starts up another debugger). We create a new IN and +OUT filehandle, and do the necessary mojo to create a new TTY if we know how +and if we can. + +=cut + + # Non-socket. + else { + + # Two debuggers running (probably a system or a backtick that invokes + # the debugger itself under the running one). create a new IN and OUT + # filehandle, and do the necessary mojo to create a new tty if we + # know how, and we can. + create_IN_OUT(4) if $CreateTTY & 4; + if ($console) { + + # If we have a console, check to see if there are separate ins and + # outs to open. (They are assumed identiical if not.) + + my ( $i, $o ) = split /,/, $console; + $o = $i unless defined $o; + + # read/write on in, or just read, or read on STDIN. + open( IN, "+<$i" ) + || open( IN, "<$i" ) + || open( IN, "<&STDIN" ); + + # read/write/create/clobber out, or write/create/clobber out, + # or merge with STDERR, or merge with STDOUT. + open( OUT, "+>$o" ) + || open( OUT, ">$o" ) + || open( OUT, ">&STDERR" ) + || open( OUT, ">&STDOUT" ); # so we don't dongle stdout + + } ## end if ($console) + elsif ( not defined $console ) { + + # No console. Open STDIN. + open( IN, "<&STDIN" ); + + # merge with STDERR, or with STDOUT. + open( OUT, ">&STDERR" ) + || open( OUT, ">&STDOUT" ); # so we don't dongle stdout + $console = 'STDIN/OUT'; + } ## end elsif (not defined $console) + + # Keep copies of the filehandles so that when the pager runs, it + # can close standard input without clobbering ours. + $IN = \*IN, $OUT = \*OUT if $console or not defined $console; + } ## end elsif (from if(defined $remoteport)) + + # Unbuffer DB::OUT. We need to see responses right away. + my $previous = select($OUT); + $| = 1; # for DB::OUT + select($previous); + + # Line info goes to debugger output unless pointed elsewhere. + # Pointing elsewhere makes it possible for slave editors to + # keep track of file and position. We have both a filehandle + # and a I/O description to keep track of. + $LINEINFO = $OUT unless defined $LINEINFO; + $lineinfo = $console unless defined $lineinfo; + # share($LINEINFO); # <- unable to share globs + share($lineinfo); # + +=pod + +To finish initialization, we show the debugger greeting, +and then call the C subroutine if there is one. + +=cut + + # Show the debugger greeting. + $header =~ s/.Header: ([^,]+),v(\s+\S+\s+\S+).*$/$1$2/; + unless ($runnonstop) { + local $\ = ''; + local $, = ''; + if ( $term_pid eq '-1' ) { + print $OUT "\nDaughter DB session started...\n"; + } + else { + print $OUT "\nLoading DB routines from $header\n"; + print $OUT ( + "Editor support ", + $slave_editor ? "enabled" : "available", ".\n" + ); + print $OUT +"\nEnter h or `h h' for help, or `$doccmd perldebug' for more help.\n\n"; + } ## end else [ if ($term_pid eq '-1') + } ## end unless ($runnonstop) +} ## end else [ if ($notty) + +# XXX This looks like a bug to me. +# Why copy to @ARGS and then futz with @args? +@ARGS = @ARGV; +for (@args) { + # Make sure backslashes before single quotes are stripped out, and + # keep args unless they are numeric (XXX why?) + # s/\'/\\\'/g; # removed while not justified understandably + # s/(.*)/'$1'/ unless /^-?[\d.]+$/; # ditto +} + +# If there was an afterinit() sub defined, call it. It will get +# executed in our scope, so it can fiddle with debugger globals. +if ( defined &afterinit ) { # May be defined in $rcfile + &afterinit(); +} + +# Inform us about "Stack dump during die enabled ..." in dieLevel(). +$I_m_init = 1; + + diff --git a/tests/examplefiles/perl_regex-delims b/tests/examplefiles/perl_regex-delims new file mode 100644 index 0000000..6da5298 --- /dev/null +++ b/tests/examplefiles/perl_regex-delims @@ -0,0 +1,120 @@ +#! /usr/bin/env perl + +use strict; +use warnings; + +# common delimiters +print "a: "; +my $a = "foo"; +print $a, " - "; +$a =~ s/foo/bar/; +print $a, "\n"; + +print "b: "; +my $b = "foo"; +print $b, " - "; +$b =~ s!foo!bar!; +print $b, "\n"; + +print "c: "; +my $c = "foo"; +print $c, " - "; +$c =~ s@foo@bar@; +print $c, "\n"; + +print "d: "; +my $d = "foo"; +print $d, " - "; +$d =~ s\foo\bar\; +print $d, "\n"; + +print "\n"; + +# balanced delimiters +print "e: "; +my $e = "foo"; +print $e, " - "; +$e =~ s{foo}{bar}; +print $e, "\n"; + +print "f: "; +my $f = "foo"; +print $f, " - "; +$f =~ s(foo)(bar); +print $f, "\n"; + +print "g: "; +my $g = "foo"; +print $g, " - "; +$g =~ s; +print $g, "\n"; + +print "h: "; +my $h = "foo"; +print $h, " - "; +$h =~ s[foo][bar]; +print $h, "\n"; + +print "\n"; + +# balanced delimiters with whitespace +print "i: "; +my $i = "foo"; +print $i, " - "; +$i =~ s{foo} {bar}; +print $i, "\n"; + +print "j: "; +my $j = "foo"; +print $j, " - "; +$j =~ s ; +print $j, "\n"; + +print "k: "; +my $k = "foo"; +print $k, " - "; +$k =~ + s(foo) + + (bar); +print $k, "\n"; + +print "\n"; + +# mixed delimiters +print "l: "; +my $l = "foo"; +print $l, " - "; +$l =~ s{foo} ; +print $l, "\n"; + +print "m: "; +my $m = "foo"; +print $m, " - "; +$m =~ s(foo) !bar!; +print $m, "\n"; + +print "n: "; +my $n = "foo"; +print $n, " - "; +$n =~ s[foo] $bar$; +print $n, "\n"; + +print "\n"; + +# /x modifier +print "o: "; +my $o = "foo"; +print $o, " - "; +$o =~ s{ + foo + } {bar}x; +print $o, "\n"; + +print "p: "; +my $p = "foo"; +print $p, " - "; +$p =~ s% + foo + %bar%x; +print $p, "\n"; diff --git a/tests/examplefiles/perlfunc.1 b/tests/examplefiles/perlfunc.1 new file mode 100644 index 0000000..5f80f0d --- /dev/null +++ b/tests/examplefiles/perlfunc.1 @@ -0,0 +1,856 @@ +.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32 +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sh \" Subsection heading +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. | will give a +.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to +.\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' +.\" expand to `' in nroff, nothing in troff, for use with C<>. +.tr \(*W-|\(bv\*(Tr +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.\" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.hy 0 +.if n .na +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "PERLFUNC 1" +.TH PERLFUNC 1 "2006-01-07" "perl v5.8.8" "Perl Programmers Reference Guide" +.SH "NAME" +.IX Xref "function" +perlfunc \- Perl builtin functions +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +The functions in this section can serve as terms in an expression. +They fall into two major categories: list operators and named unary +operators. These differ in their precedence relationship with a +following comma. (See the precedence table in perlop.) List +operators take more than one argument, while unary operators can never +take more than one argument. Thus, a comma terminates the argument of +a unary operator, but merely separates the arguments of a list +operator. A unary operator generally provides a scalar context to its +argument, while a list operator may provide either scalar or list +contexts for its arguments. If it does both, the scalar arguments will +be first, and the list argument will follow. (Note that there can ever +be only one such list argument.) For instance, \fIsplice()\fR has three scalar +arguments followed by a list, whereas \fIgethostbyname()\fR has four scalar +arguments. +.PP +In the syntax descriptions that follow, list operators that expect a +list (and provide list context for the elements of the list) are shown +with \s-1LIST\s0 as an argument. Such a list may consist of any combination +of scalar arguments or list values; the list values will be included +in the list as if each individual element were interpolated at that +point in the list, forming a longer single-dimensional list value. +Commas should separate elements of the \s-1LIST\s0. +.PP +Any function in the list below may be used either with or without +parentheses around its arguments. (The syntax descriptions omit the +parentheses.) If you use the parentheses, the simple (but occasionally +surprising) rule is this: It \fIlooks\fR like a function, therefore it \fIis\fR a +function, and precedence doesn't matter. Otherwise it's a list +operator or unary operator, and precedence does matter. And whitespace +between the function and left parenthesis doesn't count\*(--so you need to +be careful sometimes: +.PP +.Vb 5 +\& print 1+2+4; # Prints 7. +\& print(1+2) + 4; # Prints 3. +\& print (1+2)+4; # Also prints 3! +\& print +(1+2)+4; # Prints 7. +\& print ((1+2)+4); # Prints 7. +.Ve +.PP +If you run Perl with the \fB\-w\fR switch it can warn you about this. For +example, the third line above produces: +.PP +.Vb 2 +\& print (...) interpreted as function at - line 1. +\& Useless use of integer addition in void context at - line 1. +.Ve +.PP +A few functions take no arguments at all, and therefore work as neither +unary nor list operators. These include such functions as \f(CW\*(C`time\*(C'\fR +and \f(CW\*(C`endpwent\*(C'\fR. For example, \f(CW\*(C`time+86_400\*(C'\fR always means +\&\f(CW\*(C`time() + 86_400\*(C'\fR. +.PP +For functions that can be used in either a scalar or list context, +nonabortive failure is generally indicated in a scalar context by +returning the undefined value, and in a list context by returning the +null list. +.PP +Remember the following important rule: There is \fBno rule\fR that relates +the behavior of an expression in list context to its behavior in scalar +context, or vice versa. It might do two totally different things. +Each operator and function decides which sort of value it would be most +appropriate to return in scalar context. Some operators return the +length of the list that would have been returned in list context. Some +operators return the first value in the list. Some operators return the +last value in the list. Some operators return a count of successful +operations. In general, they do what you want, unless you want +consistency. +.IX Xref "context" +.PP +A named array in scalar context is quite different from what would at +first glance appear to be a list in scalar context. You can't get a list +like \f(CW\*(C`(1,2,3)\*(C'\fR into being in scalar context, because the compiler knows +the context at compile time. It would generate the scalar comma operator +there, not the list construction version of the comma. That means it +was never a list to start with. +.PP +In general, functions in Perl that serve as wrappers for system calls +of the same name (like \fIchown\fR\|(2), \fIfork\fR\|(2), \fIclosedir\fR\|(2), etc.) all return +true when they succeed and \f(CW\*(C`undef\*(C'\fR otherwise, as is usually mentioned +in the descriptions below. This is different from the C interfaces, +which return \f(CW\*(C`\-1\*(C'\fR on failure. Exceptions to this rule are \f(CW\*(C`wait\*(C'\fR, +\&\f(CW\*(C`waitpid\*(C'\fR, and \f(CW\*(C`syscall\*(C'\fR. System calls also set the special \f(CW$!\fR +variable on failure. Other functions do not, except accidentally. +.Sh "Perl Functions by Category" +.IX Xref "function" +.IX Subsection "Perl Functions by Category" +Here are Perl's functions (including things that look like +functions, like some keywords and named operators) +arranged by category. Some functions appear in more +than one place. +.IP "Functions for SCALARs or strings" 4 +.IX Xref "scalar string character" +.IX Item "Functions for SCALARs or strings" +\&\f(CW\*(C`chomp\*(C'\fR, \f(CW\*(C`chop\*(C'\fR, \f(CW\*(C`chr\*(C'\fR, \f(CW\*(C`crypt\*(C'\fR, \f(CW\*(C`hex\*(C'\fR, \f(CW\*(C`index\*(C'\fR, \f(CW\*(C`lc\*(C'\fR, \f(CW\*(C`lcfirst\*(C'\fR, +\&\f(CW\*(C`length\*(C'\fR, \f(CW\*(C`oct\*(C'\fR, \f(CW\*(C`ord\*(C'\fR, \f(CW\*(C`pack\*(C'\fR, \f(CW\*(C`q/STRING/\*(C'\fR, \f(CW\*(C`qq/STRING/\*(C'\fR, \f(CW\*(C`reverse\*(C'\fR, +\&\f(CW\*(C`rindex\*(C'\fR, \f(CW\*(C`sprintf\*(C'\fR, \f(CW\*(C`substr\*(C'\fR, \f(CW\*(C`tr///\*(C'\fR, \f(CW\*(C`uc\*(C'\fR, \f(CW\*(C`ucfirst\*(C'\fR, \f(CW\*(C`y///\*(C'\fR +.IP "Regular expressions and pattern matching" 4 +.IX Xref "regular expression regex regexp" +.IX Item "Regular expressions and pattern matching" +\&\f(CW\*(C`m//\*(C'\fR, \f(CW\*(C`pos\*(C'\fR, \f(CW\*(C`quotemeta\*(C'\fR, \f(CW\*(C`s///\*(C'\fR, \f(CW\*(C`split\*(C'\fR, \f(CW\*(C`study\*(C'\fR, \f(CW\*(C`qr//\*(C'\fR +.IP "Numeric functions" 4 +.IX Xref "numeric number trigonometric trigonometry" +.IX Item "Numeric functions" +\&\f(CW\*(C`abs\*(C'\fR, \f(CW\*(C`atan2\*(C'\fR, \f(CW\*(C`cos\*(C'\fR, \f(CW\*(C`exp\*(C'\fR, \f(CW\*(C`hex\*(C'\fR, \f(CW\*(C`int\*(C'\fR, \f(CW\*(C`log\*(C'\fR, \f(CW\*(C`oct\*(C'\fR, \f(CW\*(C`rand\*(C'\fR, +\&\f(CW\*(C`sin\*(C'\fR, \f(CW\*(C`sqrt\*(C'\fR, \f(CW\*(C`srand\*(C'\fR +.ie n .IP "Functions for real @ARRAYs" 4 +.el .IP "Functions for real \f(CW@ARRAYs\fR" 4 +.IX Xref "array" +.IX Item "Functions for real @ARRAYs" +\&\f(CW\*(C`pop\*(C'\fR, \f(CW\*(C`push\*(C'\fR, \f(CW\*(C`shift\*(C'\fR, \f(CW\*(C`splice\*(C'\fR, \f(CW\*(C`unshift\*(C'\fR +.IP "Functions for list data" 4 +.IX Xref "list" +.IX Item "Functions for list data" +\&\f(CW\*(C`grep\*(C'\fR, \f(CW\*(C`join\*(C'\fR, \f(CW\*(C`map\*(C'\fR, \f(CW\*(C`qw/STRING/\*(C'\fR, \f(CW\*(C`reverse\*(C'\fR, \f(CW\*(C`sort\*(C'\fR, \f(CW\*(C`unpack\*(C'\fR +.ie n .IP "Functions for real %HASHes" 4 +.el .IP "Functions for real \f(CW%HASHes\fR" 4 +.IX Xref "hash" +.IX Item "Functions for real %HASHes" +\&\f(CW\*(C`delete\*(C'\fR, \f(CW\*(C`each\*(C'\fR, \f(CW\*(C`exists\*(C'\fR, \f(CW\*(C`keys\*(C'\fR, \f(CW\*(C`values\*(C'\fR +.IP "Input and output functions" 4 +.IX Xref "I O input output dbm" +.IX Item "Input and output functions" +\&\f(CW\*(C`binmode\*(C'\fR, \f(CW\*(C`close\*(C'\fR, \f(CW\*(C`closedir\*(C'\fR, \f(CW\*(C`dbmclose\*(C'\fR, \f(CW\*(C`dbmopen\*(C'\fR, \f(CW\*(C`die\*(C'\fR, \f(CW\*(C`eof\*(C'\fR, +\&\f(CW\*(C`fileno\*(C'\fR, \f(CW\*(C`flock\*(C'\fR, \f(CW\*(C`format\*(C'\fR, \f(CW\*(C`getc\*(C'\fR, \f(CW\*(C`print\*(C'\fR, \f(CW\*(C`printf\*(C'\fR, \f(CW\*(C`read\*(C'\fR, +\&\f(CW\*(C`readdir\*(C'\fR, \f(CW\*(C`rewinddir\*(C'\fR, \f(CW\*(C`seek\*(C'\fR, \f(CW\*(C`seekdir\*(C'\fR, \f(CW\*(C`select\*(C'\fR, \f(CW\*(C`syscall\*(C'\fR, +\&\f(CW\*(C`sysread\*(C'\fR, \f(CW\*(C`sysseek\*(C'\fR, \f(CW\*(C`syswrite\*(C'\fR, \f(CW\*(C`tell\*(C'\fR, \f(CW\*(C`telldir\*(C'\fR, \f(CW\*(C`truncate\*(C'\fR, +\&\f(CW\*(C`warn\*(C'\fR, \f(CW\*(C`write\*(C'\fR +.IP "Functions for fixed length data or records" 4 +.IX Item "Functions for fixed length data or records" +\&\f(CW\*(C`pack\*(C'\fR, \f(CW\*(C`read\*(C'\fR, \f(CW\*(C`syscall\*(C'\fR, \f(CW\*(C`sysread\*(C'\fR, \f(CW\*(C`syswrite\*(C'\fR, \f(CW\*(C`unpack\*(C'\fR, \f(CW\*(C`vec\*(C'\fR +.IP "Functions for filehandles, files, or directories" 4 +.IX Xref "file filehandle directory pipe link symlink" +.IX Item "Functions for filehandles, files, or directories" +\&\f(CW\*(C`\-\f(CIX\f(CW\*(C'\fR, \f(CW\*(C`chdir\*(C'\fR, \f(CW\*(C`chmod\*(C'\fR, \f(CW\*(C`chown\*(C'\fR, \f(CW\*(C`chroot\*(C'\fR, \f(CW\*(C`fcntl\*(C'\fR, \f(CW\*(C`glob\*(C'\fR, +\&\f(CW\*(C`ioctl\*(C'\fR, \f(CW\*(C`link\*(C'\fR, \f(CW\*(C`lstat\*(C'\fR, \f(CW\*(C`mkdir\*(C'\fR, \f(CW\*(C`open\*(C'\fR, \f(CW\*(C`opendir\*(C'\fR, +\&\f(CW\*(C`readlink\*(C'\fR, \f(CW\*(C`rename\*(C'\fR, \f(CW\*(C`rmdir\*(C'\fR, \f(CW\*(C`stat\*(C'\fR, \f(CW\*(C`symlink\*(C'\fR, \f(CW\*(C`sysopen\*(C'\fR, +\&\f(CW\*(C`umask\*(C'\fR, \f(CW\*(C`unlink\*(C'\fR, \f(CW\*(C`utime\*(C'\fR +.IP "Keywords related to the control flow of your Perl program" 4 +.IX Xref "control flow" +.IX Item "Keywords related to the control flow of your Perl program" +\&\f(CW\*(C`caller\*(C'\fR, \f(CW\*(C`continue\*(C'\fR, \f(CW\*(C`die\*(C'\fR, \f(CW\*(C`do\*(C'\fR, \f(CW\*(C`dump\*(C'\fR, \f(CW\*(C`eval\*(C'\fR, \f(CW\*(C`exit\*(C'\fR, +\&\f(CW\*(C`goto\*(C'\fR, \f(CW\*(C`last\*(C'\fR, \f(CW\*(C`next\*(C'\fR, \f(CW\*(C`redo\*(C'\fR, \f(CW\*(C`return\*(C'\fR, \f(CW\*(C`sub\*(C'\fR, \f(CW\*(C`wantarray\*(C'\fR +.IP "Keywords related to scoping" 4 +.IX Item "Keywords related to scoping" +\&\f(CW\*(C`caller\*(C'\fR, \f(CW\*(C`import\*(C'\fR, \f(CW\*(C`local\*(C'\fR, \f(CW\*(C`my\*(C'\fR, \f(CW\*(C`our\*(C'\fR, \f(CW\*(C`package\*(C'\fR, \f(CW\*(C`use\*(C'\fR +.IP "Miscellaneous functions" 4 +.IX Item "Miscellaneous functions" +\&\f(CW\*(C`defined\*(C'\fR, \f(CW\*(C`dump\*(C'\fR, \f(CW\*(C`eval\*(C'\fR, \f(CW\*(C`formline\*(C'\fR, \f(CW\*(C`local\*(C'\fR, \f(CW\*(C`my\*(C'\fR, \f(CW\*(C`our\*(C'\fR, \f(CW\*(C`reset\*(C'\fR, +\&\f(CW\*(C`scalar\*(C'\fR, \f(CW\*(C`undef\*(C'\fR, \f(CW\*(C`wantarray\*(C'\fR +.IP "Functions for processes and process groups" 4 +.IX Xref "process pid process id" +.IX Item "Functions for processes and process groups" +\&\f(CW\*(C`alarm\*(C'\fR, \f(CW\*(C`exec\*(C'\fR, \f(CW\*(C`fork\*(C'\fR, \f(CW\*(C`getpgrp\*(C'\fR, \f(CW\*(C`getppid\*(C'\fR, \f(CW\*(C`getpriority\*(C'\fR, \f(CW\*(C`kill\*(C'\fR, +\&\f(CW\*(C`pipe\*(C'\fR, \f(CW\*(C`qx/STRING/\*(C'\fR, \f(CW\*(C`setpgrp\*(C'\fR, \f(CW\*(C`setpriority\*(C'\fR, \f(CW\*(C`sleep\*(C'\fR, \f(CW\*(C`system\*(C'\fR, +\&\f(CW\*(C`times\*(C'\fR, \f(CW\*(C`wait\*(C'\fR, \f(CW\*(C`waitpid\*(C'\fR +.IP "Keywords related to perl modules" 4 +.IX Xref "module" +.IX Item "Keywords related to perl modules" +\&\f(CW\*(C`do\*(C'\fR, \f(CW\*(C`import\*(C'\fR, \f(CW\*(C`no\*(C'\fR, \f(CW\*(C`package\*(C'\fR, \f(CW\*(C`require\*(C'\fR, \f(CW\*(C`use\*(C'\fR +.IP "Keywords related to classes and object-orientedness" 4 +.IX Xref "object class package" +.IX Item "Keywords related to classes and object-orientedness" +\&\f(CW\*(C`bless\*(C'\fR, \f(CW\*(C`dbmclose\*(C'\fR, \f(CW\*(C`dbmopen\*(C'\fR, \f(CW\*(C`package\*(C'\fR, \f(CW\*(C`ref\*(C'\fR, \f(CW\*(C`tie\*(C'\fR, \f(CW\*(C`tied\*(C'\fR, +\&\f(CW\*(C`untie\*(C'\fR, \f(CW\*(C`use\*(C'\fR +.IP "Low-level socket functions" 4 +.IX Xref "socket sock" +.IX Item "Low-level socket functions" +\&\f(CW\*(C`accept\*(C'\fR, \f(CW\*(C`bind\*(C'\fR, \f(CW\*(C`connect\*(C'\fR, \f(CW\*(C`getpeername\*(C'\fR, \f(CW\*(C`getsockname\*(C'\fR, +\&\f(CW\*(C`getsockopt\*(C'\fR, \f(CW\*(C`listen\*(C'\fR, \f(CW\*(C`recv\*(C'\fR, \f(CW\*(C`send\*(C'\fR, \f(CW\*(C`setsockopt\*(C'\fR, \f(CW\*(C`shutdown\*(C'\fR, +\&\f(CW\*(C`socket\*(C'\fR, \f(CW\*(C`socketpair\*(C'\fR +.IP "System V interprocess communication functions" 4 +.IX Xref "IPC System V semaphore shared memory memory message" +.IX Item "System V interprocess communication functions" +\&\f(CW\*(C`msgctl\*(C'\fR, \f(CW\*(C`msgget\*(C'\fR, \f(CW\*(C`msgrcv\*(C'\fR, \f(CW\*(C`msgsnd\*(C'\fR, \f(CW\*(C`semctl\*(C'\fR, \f(CW\*(C`semget\*(C'\fR, \f(CW\*(C`semop\*(C'\fR, +\&\f(CW\*(C`shmctl\*(C'\fR, \f(CW\*(C`shmget\*(C'\fR, \f(CW\*(C`shmread\*(C'\fR, \f(CW\*(C`shmwrite\*(C'\fR +.IP "Fetching user and group info" 4 +.IX Xref "user group password uid gid passwd etc passwd" +.IX Item "Fetching user and group info" +\&\f(CW\*(C`endgrent\*(C'\fR, \f(CW\*(C`endhostent\*(C'\fR, \f(CW\*(C`endnetent\*(C'\fR, \f(CW\*(C`endpwent\*(C'\fR, \f(CW\*(C`getgrent\*(C'\fR, +\&\f(CW\*(C`getgrgid\*(C'\fR, \f(CW\*(C`getgrnam\*(C'\fR, \f(CW\*(C`getlogin\*(C'\fR, \f(CW\*(C`getpwent\*(C'\fR, \f(CW\*(C`getpwnam\*(C'\fR, +\&\f(CW\*(C`getpwuid\*(C'\fR, \f(CW\*(C`setgrent\*(C'\fR, \f(CW\*(C`setpwent\*(C'\fR +.IP "Fetching network info" 4 +.IX Xref "network protocol host hostname IP address service" +.IX Item "Fetching network info" +\&\f(CW\*(C`endprotoent\*(C'\fR, \f(CW\*(C`endservent\*(C'\fR, \f(CW\*(C`gethostbyaddr\*(C'\fR, \f(CW\*(C`gethostbyname\*(C'\fR, +\&\f(CW\*(C`gethostent\*(C'\fR, \f(CW\*(C`getnetbyaddr\*(C'\fR, \f(CW\*(C`getnetbyname\*(C'\fR, \f(CW\*(C`getnetent\*(C'\fR, +\&\f(CW\*(C`getprotobyname\*(C'\fR, \f(CW\*(C`getprotobynumber\*(C'\fR, \f(CW\*(C`getprotoent\*(C'\fR, +\&\f(CW\*(C`getservbyname\*(C'\fR, \f(CW\*(C`getservbyport\*(C'\fR, \f(CW\*(C`getservent\*(C'\fR, \f(CW\*(C`sethostent\*(C'\fR, +\&\f(CW\*(C`setnetent\*(C'\fR, \f(CW\*(C`setprotoent\*(C'\fR, \f(CW\*(C`setservent\*(C'\fR +.IP "Time-related functions" 4 +.IX Xref "time date" +.IX Item "Time-related functions" +\&\f(CW\*(C`gmtime\*(C'\fR, \f(CW\*(C`localtime\*(C'\fR, \f(CW\*(C`time\*(C'\fR, \f(CW\*(C`times\*(C'\fR +.IP "Functions new in perl5" 4 +.IX Xref "perl5" +.IX Item "Functions new in perl5" +\&\f(CW\*(C`abs\*(C'\fR, \f(CW\*(C`bless\*(C'\fR, \f(CW\*(C`chomp\*(C'\fR, \f(CW\*(C`chr\*(C'\fR, \f(CW\*(C`exists\*(C'\fR, \f(CW\*(C`formline\*(C'\fR, \f(CW\*(C`glob\*(C'\fR, +\&\f(CW\*(C`import\*(C'\fR, \f(CW\*(C`lc\*(C'\fR, \f(CW\*(C`lcfirst\*(C'\fR, \f(CW\*(C`map\*(C'\fR, \f(CW\*(C`my\*(C'\fR, \f(CW\*(C`no\*(C'\fR, \f(CW\*(C`our\*(C'\fR, \f(CW\*(C`prototype\*(C'\fR, +\&\f(CW\*(C`qx\*(C'\fR, \f(CW\*(C`qw\*(C'\fR, \f(CW\*(C`readline\*(C'\fR, \f(CW\*(C`readpipe\*(C'\fR, \f(CW\*(C`ref\*(C'\fR, \f(CW\*(C`sub*\*(C'\fR, \f(CW\*(C`sysopen\*(C'\fR, \f(CW\*(C`tie\*(C'\fR, +\&\f(CW\*(C`tied\*(C'\fR, \f(CW\*(C`uc\*(C'\fR, \f(CW\*(C`ucfirst\*(C'\fR, \f(CW\*(C`untie\*(C'\fR, \f(CW\*(C`use\*(C'\fR +.Sp +* \- \f(CW\*(C`sub\*(C'\fR was a keyword in perl4, but in perl5 it is an +operator, which can be used in expressions. +.IP "Functions obsoleted in perl5" 4 +.IX Item "Functions obsoleted in perl5" +\&\f(CW\*(C`dbmclose\*(C'\fR, \f(CW\*(C`dbmopen\*(C'\fR +.Sh "Portability" +.IX Xref "portability Unix portable" +.IX Subsection "Portability" +Perl was born in Unix and can therefore access all common Unix +system calls. In non-Unix environments, the functionality of some +Unix system calls may not be available, or details of the available +functionality may differ slightly. The Perl functions affected +by this are: +.PP +\&\f(CW\*(C`\-X\*(C'\fR, \f(CW\*(C`binmode\*(C'\fR, \f(CW\*(C`chmod\*(C'\fR, \f(CW\*(C`chown\*(C'\fR, \f(CW\*(C`chroot\*(C'\fR, \f(CW\*(C`crypt\*(C'\fR, +\&\f(CW\*(C`dbmclose\*(C'\fR, \f(CW\*(C`dbmopen\*(C'\fR, \f(CW\*(C`dump\*(C'\fR, \f(CW\*(C`endgrent\*(C'\fR, \f(CW\*(C`endhostent\*(C'\fR, +\&\f(CW\*(C`endnetent\*(C'\fR, \f(CW\*(C`endprotoent\*(C'\fR, \f(CW\*(C`endpwent\*(C'\fR, \f(CW\*(C`endservent\*(C'\fR, \f(CW\*(C`exec\*(C'\fR, +\&\f(CW\*(C`fcntl\*(C'\fR, \f(CW\*(C`flock\*(C'\fR, \f(CW\*(C`fork\*(C'\fR, \f(CW\*(C`getgrent\*(C'\fR, \f(CW\*(C`getgrgid\*(C'\fR, \f(CW\*(C`gethostbyname\*(C'\fR, +\&\f(CW\*(C`gethostent\*(C'\fR, \f(CW\*(C`getlogin\*(C'\fR, \f(CW\*(C`getnetbyaddr\*(C'\fR, \f(CW\*(C`getnetbyname\*(C'\fR, \f(CW\*(C`getnetent\*(C'\fR, +\&\f(CW\*(C`getppid\*(C'\fR, \f(CW\*(C`getpgrp\*(C'\fR, \f(CW\*(C`getpriority\*(C'\fR, \f(CW\*(C`getprotobynumber\*(C'\fR, +\&\f(CW\*(C`getprotoent\*(C'\fR, \f(CW\*(C`getpwent\*(C'\fR, \f(CW\*(C`getpwnam\*(C'\fR, \f(CW\*(C`getpwuid\*(C'\fR, +\&\f(CW\*(C`getservbyport\*(C'\fR, \f(CW\*(C`getservent\*(C'\fR, \f(CW\*(C`getsockopt\*(C'\fR, \f(CW\*(C`glob\*(C'\fR, \f(CW\*(C`ioctl\*(C'\fR, +\&\f(CW\*(C`kill\*(C'\fR, \f(CW\*(C`link\*(C'\fR, \f(CW\*(C`lstat\*(C'\fR, \f(CW\*(C`msgctl\*(C'\fR, \f(CW\*(C`msgget\*(C'\fR, \f(CW\*(C`msgrcv\*(C'\fR, +\&\f(CW\*(C`msgsnd\*(C'\fR, \f(CW\*(C`open\*(C'\fR, \f(CW\*(C`pipe\*(C'\fR, \f(CW\*(C`readlink\*(C'\fR, \f(CW\*(C`rename\*(C'\fR, \f(CW\*(C`select\*(C'\fR, \f(CW\*(C`semctl\*(C'\fR, +\&\f(CW\*(C`semget\*(C'\fR, \f(CW\*(C`semop\*(C'\fR, \f(CW\*(C`setgrent\*(C'\fR, \f(CW\*(C`sethostent\*(C'\fR, \f(CW\*(C`setnetent\*(C'\fR, +\&\f(CW\*(C`setpgrp\*(C'\fR, \f(CW\*(C`setpriority\*(C'\fR, \f(CW\*(C`setprotoent\*(C'\fR, \f(CW\*(C`setpwent\*(C'\fR, +\&\f(CW\*(C`setservent\*(C'\fR, \f(CW\*(C`setsockopt\*(C'\fR, \f(CW\*(C`shmctl\*(C'\fR, \f(CW\*(C`shmget\*(C'\fR, \f(CW\*(C`shmread\*(C'\fR, +\&\f(CW\*(C`shmwrite\*(C'\fR, \f(CW\*(C`socket\*(C'\fR, \f(CW\*(C`socketpair\*(C'\fR, +\&\f(CW\*(C`stat\*(C'\fR, \f(CW\*(C`symlink\*(C'\fR, \f(CW\*(C`syscall\*(C'\fR, \f(CW\*(C`sysopen\*(C'\fR, \f(CW\*(C`system\*(C'\fR, +\&\f(CW\*(C`times\*(C'\fR, \f(CW\*(C`truncate\*(C'\fR, \f(CW\*(C`umask\*(C'\fR, \f(CW\*(C`unlink\*(C'\fR, +\&\f(CW\*(C`utime\*(C'\fR, \f(CW\*(C`wait\*(C'\fR, \f(CW\*(C`waitpid\*(C'\fR +.PP +For more information about the portability of these functions, see +perlport and other available platform-specific documentation. +.Sh "Alphabetical Listing of Perl Functions" +.IX Subsection "Alphabetical Listing of Perl Functions" +.IP "\-X \s-1FILEHANDLE\s0" 8 +.IX Xref "-r -w -x -o -R -W -X -O -e -z -s -f -d -l -p -S -b -c -t -u -g -k -T -B -M -A -C" +.IX Item "-X FILEHANDLE" +.PD 0 +.IP "\-X \s-1EXPR\s0" 8 +.IX Item "-X EXPR" +.IP "\-X" 8 +.IX Item "-X" +.PD +A file test, where X is one of the letters listed below. This unary +operator takes one argument, either a filename or a filehandle, and +tests the associated file to see if something is true about it. If the +argument is omitted, tests \f(CW$_\fR, except for \f(CW\*(C`\-t\*(C'\fR, which tests \s-1STDIN\s0. +Unless otherwise documented, it returns \f(CW1\fR for true and \f(CW''\fR for false, or +the undefined value if the file doesn't exist. Despite the funny +names, precedence is the same as any other named unary operator, and +the argument may be parenthesized like any other unary operator. The +operator may be any of: +.Sp +.Vb 4 +\& -r File is readable by effective uid/gid. +\& -w File is writable by effective uid/gid. +\& -x File is executable by effective uid/gid. +\& -o File is owned by effective uid. +.Ve +.Sp +.Vb 4 +\& -R File is readable by real uid/gid. +\& -W File is writable by real uid/gid. +\& -X File is executable by real uid/gid. +\& -O File is owned by real uid. +.Ve +.Sp +.Vb 3 +\& -e File exists. +\& -z File has zero size (is empty). +\& -s File has nonzero size (returns size in bytes). +.Ve +.Sp +.Vb 8 +\& -f File is a plain file. +\& -d File is a directory. +\& -l File is a symbolic link. +\& -p File is a named pipe (FIFO), or Filehandle is a pipe. +\& -S File is a socket. +\& -b File is a block special file. +\& -c File is a character special file. +\& -t Filehandle is opened to a tty. +.Ve +.Sp +.Vb 3 +\& -u File has setuid bit set. +\& -g File has setgid bit set. +\& -k File has sticky bit set. +.Ve +.Sp +.Vb 2 +\& -T File is an ASCII text file (heuristic guess). +\& -B File is a "binary" file (opposite of -T). +.Ve +.Sp +.Vb 3 +\& -M Script start time minus file modification time, in days. +\& -A Same for access time. +\& -C Same for inode change time (Unix, may differ for other platforms) +.Ve +.Sp +Example: +.Sp +.Vb 5 +\& while (<>) { +\& chomp; +\& next unless -f $_; # ignore specials +\& #... +\& } +.Ve +.Sp +The interpretation of the file permission operators \f(CW\*(C`\-r\*(C'\fR, \f(CW\*(C`\-R\*(C'\fR, +\&\f(CW\*(C`\-w\*(C'\fR, \f(CW\*(C`\-W\*(C'\fR, \f(CW\*(C`\-x\*(C'\fR, and \f(CW\*(C`\-X\*(C'\fR is by default based solely on the mode +of the file and the uids and gids of the user. There may be other +reasons you can't actually read, write, or execute the file. Such +reasons may be for example network filesystem access controls, ACLs +(access control lists), read-only filesystems, and unrecognized +executable formats. +.Sp +Also note that, for the superuser on the local filesystems, the \f(CW\*(C`\-r\*(C'\fR, +\&\f(CW\*(C`\-R\*(C'\fR, \f(CW\*(C`\-w\*(C'\fR, and \f(CW\*(C`\-W\*(C'\fR tests always return 1, and \f(CW\*(C`\-x\*(C'\fR and \f(CW\*(C`\-X\*(C'\fR return 1 +if any execute bit is set in the mode. Scripts run by the superuser +may thus need to do a \fIstat()\fR to determine the actual mode of the file, +or temporarily set their effective uid to something else. +.Sp +If you are using ACLs, there is a pragma called \f(CW\*(C`filetest\*(C'\fR that may +produce more accurate results than the bare \fIstat()\fR mode bits. +When under the \f(CW\*(C`use filetest 'access'\*(C'\fR the above-mentioned filetests +will test whether the permission can (not) be granted using the +\&\fIaccess()\fR family of system calls. Also note that the \f(CW\*(C`\-x\*(C'\fR and \f(CW\*(C`\-X\*(C'\fR may +under this pragma return true even if there are no execute permission +bits set (nor any extra execute permission ACLs). This strangeness is +due to the underlying system calls' definitions. Read the +documentation for the \f(CW\*(C`filetest\*(C'\fR pragma for more information. +.Sp +Note that \f(CW\*(C`\-s/a/b/\*(C'\fR does not do a negated substitution. Saying +\&\f(CW\*(C`\-exp($foo)\*(C'\fR still works as expected, however\*(--only single letters +following a minus are interpreted as file tests. +.Sp +The \f(CW\*(C`\-T\*(C'\fR and \f(CW\*(C`\-B\*(C'\fR switches work as follows. The first block or so of the +file is examined for odd characters such as strange control codes or +characters with the high bit set. If too many strange characters (>30%) +are found, it's a \f(CW\*(C`\-B\*(C'\fR file; otherwise it's a \f(CW\*(C`\-T\*(C'\fR file. Also, any file +containing null in the first block is considered a binary file. If \f(CW\*(C`\-T\*(C'\fR +or \f(CW\*(C`\-B\*(C'\fR is used on a filehandle, the current \s-1IO\s0 buffer is examined +rather than the first block. Both \f(CW\*(C`\-T\*(C'\fR and \f(CW\*(C`\-B\*(C'\fR return true on a null +file, or a file at \s-1EOF\s0 when testing a filehandle. Because you have to +read a file to do the \f(CW\*(C`\-T\*(C'\fR test, on most occasions you want to use a \f(CW\*(C`\-f\*(C'\fR +against the file first, as in \f(CW\*(C`next unless \-f $file && \-T $file\*(C'\fR. +.Sp +If any of the file tests (or either the \f(CW\*(C`stat\*(C'\fR or \f(CW\*(C`lstat\*(C'\fR operators) are given +the special filehandle consisting of a solitary underline, then the stat +structure of the previous file test (or stat operator) is used, saving +a system call. (This doesn't work with \f(CW\*(C`\-t\*(C'\fR, and you need to remember +that \fIlstat()\fR and \f(CW\*(C`\-l\*(C'\fR will leave values in the stat structure for the +symbolic link, not the real file.) (Also, if the stat buffer was filled by +an \f(CW\*(C`lstat\*(C'\fR call, \f(CW\*(C`\-T\*(C'\fR and \f(CW\*(C`\-B\*(C'\fR will reset it with the results of \f(CW\*(C`stat _\*(C'\fR). +Example: +.Sp +.Vb 1 +\& print "Can do.\en" if -r $a || -w _ || -x _; +.Ve +.Sp +.Vb 9 +\& stat($filename); +\& print "Readable\en" if -r _; +\& print "Writable\en" if -w _; +\& print "Executable\en" if -x _; +\& print "Setuid\en" if -u _; +\& print "Setgid\en" if -g _; +\& print "Sticky\en" if -k _; +\& print "Text\en" if -T _; +\& print "Binary\en" if -B _; +.Ve +.IP "abs \s-1VALUE\s0" 8 +.IX Xref "abs absolute" +.IX Item "abs VALUE" +.PD 0 +.IP "abs" 8 +.IX Item "abs" +.PD +Returns the absolute value of its argument. +If \s-1VALUE\s0 is omitted, uses \f(CW$_\fR. +.IP "accept \s-1NEWSOCKET\s0,GENERICSOCKET" 8 +.IX Xref "accept" +.IX Item "accept NEWSOCKET,GENERICSOCKET" +Accepts an incoming socket connect, just as the \fIaccept\fR\|(2) system call +does. Returns the packed address if it succeeded, false otherwise. +See the example in \*(L"Sockets: Client/Server Communication\*(R" in perlipc. +.Sp +On systems that support a close-on-exec flag on files, the flag will +be set for the newly opened file descriptor, as determined by the +value of $^F. See \*(L"$^F\*(R" in perlvar. +.IP "alarm \s-1SECONDS\s0" 8 +.IX Xref "alarm SIGALRM timer" +.IX Item "alarm SECONDS" +.PD 0 +.IP "alarm" 8 +.IX Item "alarm" +.PD +Arranges to have a \s-1SIGALRM\s0 delivered to this process after the +specified number of wallclock seconds has elapsed. If \s-1SECONDS\s0 is not +specified, the value stored in \f(CW$_\fR is used. (On some machines, +unfortunately, the elapsed time may be up to one second less or more +than you specified because of how seconds are counted, and process +scheduling may delay the delivery of the signal even further.) +.Sp +Only one timer may be counting at once. Each call disables the +previous timer, and an argument of \f(CW0\fR may be supplied to cancel the +previous timer without starting a new one. The returned value is the +amount of time remaining on the previous timer. +.Sp +For delays of finer granularity than one second, you may use Perl's +four-argument version of \fIselect()\fR leaving the first three arguments +undefined, or you might be able to use the \f(CW\*(C`syscall\*(C'\fR interface to +access \fIsetitimer\fR\|(2) if your system supports it. The Time::HiRes +module (from \s-1CPAN\s0, and starting from Perl 5.8 part of the standard +distribution) may also prove useful. +.Sp +It is usually a mistake to intermix \f(CW\*(C`alarm\*(C'\fR and \f(CW\*(C`sleep\*(C'\fR calls. +(\f(CW\*(C`sleep\*(C'\fR may be internally implemented in your system with \f(CW\*(C`alarm\*(C'\fR) +.Sp +If you want to use \f(CW\*(C`alarm\*(C'\fR to time out a system call you need to use an +\&\f(CW\*(C`eval\*(C'\fR/\f(CW\*(C`die\*(C'\fR pair. You can't rely on the alarm causing the system call to +fail with \f(CW$!\fR set to \f(CW\*(C`EINTR\*(C'\fR because Perl sets up signal handlers to +restart system calls on some systems. Using \f(CW\*(C`eval\*(C'\fR/\f(CW\*(C`die\*(C'\fR always works, +modulo the caveats given in \*(L"Signals\*(R" in perlipc. +.Sp +.Vb 13 +\& eval { +\& local $SIG{ALRM} = sub { die "alarm\en" }; # NB: \en required +\& alarm $timeout; +\& $nread = sysread SOCKET, $buffer, $size; +\& alarm 0; +\& }; +\& if ($@) { +\& die unless $@ eq "alarm\en"; # propagate unexpected errors +\& # timed out +\& } +\& else { +\& # didn't +\& } +.Ve +.Sp +For more information see perlipc. +.IP "atan2 Y,X" 8 +.IX Xref "atan2 arctangent tan tangent" +.IX Item "atan2 Y,X" +Returns the arctangent of Y/X in the range \-PI to \s-1PI\s0. +.Sp +For the tangent operation, you may use the \f(CW\*(C`Math::Trig::tan\*(C'\fR +function, or use the familiar relation: +.Sp +.Vb 1 +\& sub tan { sin($_[0]) / cos($_[0]) } +.Ve +.Sp +Note that atan2(0, 0) is not well\-defined. +.IP "bind \s-1SOCKET\s0,NAME" 8 +.IX Xref "bind" +.IX Item "bind SOCKET,NAME" +Binds a network address to a socket, just as the bind system call +does. Returns true if it succeeded, false otherwise. \s-1NAME\s0 should be a +packed address of the appropriate type for the socket. See the examples in +\&\*(L"Sockets: Client/Server Communication\*(R" in perlipc. +.IP "binmode \s-1FILEHANDLE\s0, \s-1LAYER\s0" 8 +.IX Xref "binmode binary text DOS Windows" +.IX Item "binmode FILEHANDLE, LAYER" +.PD 0 +.IP "binmode \s-1FILEHANDLE\s0" 8 +.IX Item "binmode FILEHANDLE" +.PD +Arranges for \s-1FILEHANDLE\s0 to be read or written in \*(L"binary\*(R" or \*(L"text\*(R" +mode on systems where the run-time libraries distinguish between +binary and text files. If \s-1FILEHANDLE\s0 is an expression, the value is +taken as the name of the filehandle. Returns true on success, +otherwise it returns \f(CW\*(C`undef\*(C'\fR and sets \f(CW$!\fR (errno). +.Sp +On some systems (in general, \s-1DOS\s0 and Windows-based systems) \fIbinmode()\fR +is necessary when you're not working with a text file. For the sake +of portability it is a good idea to always use it when appropriate, +and to never use it when it isn't appropriate. Also, people can +set their I/O to be by default \s-1UTF\-8\s0 encoded Unicode, not bytes. +.Sp +In other words: regardless of platform, use \fIbinmode()\fR on binary data, +like for example images. +.Sp +If \s-1LAYER\s0 is present it is a single string, but may contain multiple +directives. The directives alter the behaviour of the file handle. +When \s-1LAYER\s0 is present using binmode on text file makes sense. +.Sp +If \s-1LAYER\s0 is omitted or specified as \f(CW\*(C`:raw\*(C'\fR the filehandle is made +suitable for passing binary data. This includes turning off possible \s-1CRLF\s0 +translation and marking it as bytes (as opposed to Unicode characters). +Note that, despite what may be implied in \fI\*(L"Programming Perl\*(R"\fR (the +Camel) or elsewhere, \f(CW\*(C`:raw\*(C'\fR is \fInot\fR the simply inverse of \f(CW\*(C`:crlf\*(C'\fR +\&\*(-- other layers which would affect binary nature of the stream are +\&\fIalso\fR disabled. See PerlIO, perlrun and the discussion about the +\&\s-1PERLIO\s0 environment variable. +.Sp +The \f(CW\*(C`:bytes\*(C'\fR, \f(CW\*(C`:crlf\*(C'\fR, and \f(CW\*(C`:utf8\*(C'\fR, and any other directives of the +form \f(CW\*(C`:...\*(C'\fR, are called I/O \fIlayers\fR. The \f(CW\*(C`open\*(C'\fR pragma can be used to +establish default I/O layers. See open. +.Sp +\&\fIThe \s-1LAYER\s0 parameter of the \fIbinmode()\fI function is described as \*(L"\s-1DISCIPLINE\s0\*(R" +in \*(L"Programming Perl, 3rd Edition\*(R". However, since the publishing of this +book, by many known as \*(L"Camel \s-1III\s0\*(R", the consensus of the naming of this +functionality has moved from \*(L"discipline\*(R" to \*(L"layer\*(R". All documentation +of this version of Perl therefore refers to \*(L"layers\*(R" rather than to +\&\*(L"disciplines\*(R". Now back to the regularly scheduled documentation...\fR +.Sp +To mark \s-1FILEHANDLE\s0 as \s-1UTF\-8\s0, use \f(CW\*(C`:utf8\*(C'\fR. +.Sp +In general, \fIbinmode()\fR should be called after \fIopen()\fR but before any I/O +is done on the filehandle. Calling \fIbinmode()\fR will normally flush any +pending buffered output data (and perhaps pending input data) on the +handle. An exception to this is the \f(CW\*(C`:encoding\*(C'\fR layer that +changes the default character encoding of the handle, see open. +The \f(CW\*(C`:encoding\*(C'\fR layer sometimes needs to be called in +mid\-stream, and it doesn't flush the stream. The \f(CW\*(C`:encoding\*(C'\fR +also implicitly pushes on top of itself the \f(CW\*(C`:utf8\*(C'\fR layer because +internally Perl will operate on \s-1UTF\-8\s0 encoded Unicode characters. +.Sp +The operating system, device drivers, C libraries, and Perl run-time +system all work together to let the programmer treat a single +character (\f(CW\*(C`\en\*(C'\fR) as the line terminator, irrespective of the external +representation. On many operating systems, the native text file +representation matches the internal representation, but on some +platforms the external representation of \f(CW\*(C`\en\*(C'\fR is made up of more than +one character. +.Sp +Mac \s-1OS\s0, all variants of Unix, and Stream_LF files on \s-1VMS\s0 use a single +character to end each line in the external representation of text (even +though that single character is \s-1CARRIAGE\s0 \s-1RETURN\s0 on Mac \s-1OS\s0 and \s-1LINE\s0 \s-1FEED\s0 +on Unix and most \s-1VMS\s0 files). In other systems like \s-1OS/2\s0, \s-1DOS\s0 and the +various flavors of MS-Windows your program sees a \f(CW\*(C`\en\*(C'\fR as a simple \f(CW\*(C`\ecJ\*(C'\fR, +but what's stored in text files are the two characters \f(CW\*(C`\ecM\ecJ\*(C'\fR. That +means that, if you don't use \fIbinmode()\fR on these systems, \f(CW\*(C`\ecM\ecJ\*(C'\fR +sequences on disk will be converted to \f(CW\*(C`\en\*(C'\fR on input, and any \f(CW\*(C`\en\*(C'\fR in +your program will be converted back to \f(CW\*(C`\ecM\ecJ\*(C'\fR on output. This is what +you want for text files, but it can be disastrous for binary files. +.Sp +Another consequence of using \fIbinmode()\fR (on some systems) is that +special end-of-file markers will be seen as part of the data stream. +For systems from the Microsoft family this means that if your binary +data contains \f(CW\*(C`\ecZ\*(C'\fR, the I/O subsystem will regard it as the end of +the file, unless you use \fIbinmode()\fR. +.Sp +\&\fIbinmode()\fR is not only important for \fIreadline()\fR and \fIprint()\fR operations, +but also when using \fIread()\fR, \fIseek()\fR, \fIsysread()\fR, \fIsyswrite()\fR and \fItell()\fR +(see perlport for more details). See the \f(CW$/\fR and \f(CW\*(C`$\e\*(C'\fR variables +in perlvar for how to manually set your input and output +line-termination sequences. +.IP "bless \s-1REF\s0,CLASSNAME" 8 +.IX Xref "bless" +.IX Item "bless REF,CLASSNAME" +.PD 0 +.IP "bless \s-1REF\s0" 8 +.IX Item "bless REF" +.PD +This function tells the thingy referenced by \s-1REF\s0 that it is now an object +in the \s-1CLASSNAME\s0 package. If \s-1CLASSNAME\s0 is omitted, the current package +is used. Because a \f(CW\*(C`bless\*(C'\fR is often the last thing in a constructor, +it returns the reference for convenience. Always use the two-argument +version if a derived class might inherit the function doing the blessing. +See perltoot and perlobj for more about the blessing (and blessings) +of objects. +.Sp +Consider always blessing objects in CLASSNAMEs that are mixed case. +Namespaces with all lowercase names are considered reserved for +Perl pragmata. Builtin types have all uppercase names. To prevent +confusion, you may wish to avoid such package names as well. Make sure +that \s-1CLASSNAME\s0 is a true value. +.Sp +See \*(L"Perl Modules\*(R" in perlmod. +.IP "caller \s-1EXPR\s0" 8 +.IX Xref "caller call stack stack stack trace" +.IX Item "caller EXPR" +.PD 0 +.IP "caller" 8 +.IX Item "caller" +.PD +Returns the context of the current subroutine call. In scalar context, +returns the caller's package name if there is a caller, that is, if +we're in a subroutine or \f(CW\*(C`eval\*(C'\fR or \f(CW\*(C`require\*(C'\fR, and the undefined value +otherwise. In list context, returns +.Sp +.Vb 1 +\& ($package, $filename, $line) = caller; +.Ve +.Sp +With \s-1EXPR\s0, it returns some extra information that the debugger uses to +print a stack trace. The value of \s-1EXPR\s0 indicates how many call frames +to go back before the current one. +.Sp +.Vb 2 +\& ($package, $filename, $line, $subroutine, $hasargs, +\& $wantarray, $evaltext, $is_require, $hints, $bitmask) = caller($i); +.Ve +.Sp +Here \f(CW$subroutine\fR may be \f(CW\*(C`(eval)\*(C'\fR if the frame is not a subroutine +call, but an \f(CW\*(C`eval\*(C'\fR. In such a case additional elements \f(CW$evaltext\fR and +\&\f(CW$is_require\fR are set: \f(CW$is_require\fR is true if the frame is created by a +\&\f(CW\*(C`require\*(C'\fR or \f(CW\*(C`use\*(C'\fR statement, \f(CW$evaltext\fR contains the text of the +\&\f(CW\*(C`eval EXPR\*(C'\fR statement. In particular, for an \f(CW\*(C`eval BLOCK\*(C'\fR statement, +\&\f(CW$filename\fR is \f(CW\*(C`(eval)\*(C'\fR, but \f(CW$evaltext\fR is undefined. (Note also that +each \f(CW\*(C`use\*(C'\fR statement creates a \f(CW\*(C`require\*(C'\fR frame inside an \f(CW\*(C`eval EXPR\*(C'\fR +frame.) \f(CW$subroutine\fR may also be \f(CW\*(C`(unknown)\*(C'\fR if this particular +subroutine happens to have been deleted from the symbol table. +\&\f(CW$hasargs\fR is true if a new instance of \f(CW@_\fR was set up for the frame. +\&\f(CW$hints\fR and \f(CW$bitmask\fR contain pragmatic hints that the caller was +compiled with. The \f(CW$hints\fR and \f(CW$bitmask\fR values are subject to change +between versions of Perl, and are not meant for external use. +.Sp +Furthermore, when called from within the \s-1DB\s0 package, caller returns more +detailed information: it sets the list variable \f(CW@DB::args\fR to be the +arguments with which the subroutine was invoked. +.Sp +Be aware that the optimizer might have optimized call frames away before +\&\f(CW\*(C`caller\*(C'\fR had a chance to get the information. That means that \f(CWcaller(N)\fR +might not return information about the call frame you expect it do, for +\&\f(CW\*(C`N > 1\*(C'\fR. In particular, \f(CW@DB::args\fR might have information from the +previous time \f(CW\*(C`caller\*(C'\fR was called. +.IP "chdir \s-1EXPR\s0" 8 +.IX Xref "chdir cd" +.IX Item "chdir EXPR" +.PD 0 +.IP "chdir \s-1FILEHANDLE\s0" 8 +.IX Item "chdir FILEHANDLE" +.IP "chdir \s-1DIRHANDLE\s0" 8 +.IX Item "chdir DIRHANDLE" +.IP "chdir" 8 +.IX Item "chdir" +.PD +Changes the working directory to \s-1EXPR\s0, if possible. If \s-1EXPR\s0 is omitted, +changes to the directory specified by \f(CW$ENV{HOME}\fR, if set; if not, +changes to the directory specified by \f(CW$ENV{LOGDIR}\fR. (Under \s-1VMS\s0, the +variable \f(CW$ENV{SYS$LOGIN}\fR is also checked, and used if it is set.) If +neither is set, \f(CW\*(C`chdir\*(C'\fR does nothing. It returns true upon success, +false otherwise. See the example under \f(CW\*(C`die\*(C'\fR. +.Sp +On systems that support fchdir, you might pass a file handle or +directory handle as argument. On systems that don't support fchdir, +passing handles produces a fatal error at run time. +.IP "chmod \s-1LIST\s0" 8 +.IX Xref "chmod permission mode" +.IX Item "chmod LIST" +Changes the permissions of a list of files. The first element of the +list must be the numerical mode, which should probably be an octal +number, and which definitely should \fInot\fR be a string of octal digits: +\&\f(CW0644\fR is okay, \f(CW'0644'\fR is not. Returns the number of files +successfully changed. See also \*(L"oct\*(R", if all you have is a string. +.Sp +.Vb 6 +\& $cnt = chmod 0755, 'foo', 'bar'; +\& chmod 0755, @executables; +\& $mode = '0644'; chmod $mode, 'foo'; # !!! sets mode to +\& # --w----r-T +\& $mode = '0644'; chmod oct($mode), 'foo'; # this is better +\& $mode = 0644; chmod $mode, 'foo'; # this is best +.Ve +.Sp +On systems that support fchmod, you might pass file handles among the +files. On systems that don't support fchmod, passing file handles +produces a fatal error at run time. +.Sp +.Vb 3 +\& open(my $fh, "<", "foo"); +\& my $perm = (stat $fh)[2] & 07777; +\& chmod($perm | 0600, $fh); +.Ve +.Sp +You can also import the symbolic \f(CW\*(C`S_I*\*(C'\fR constants from the Fcntl +module: +.Sp +.Vb 1 +\& use Fcntl ':mode'; +.Ve +.Sp +.Vb 2 +\& chmod S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, @executables; +\& # This is identical to the chmod 0755 of the above example. +.Ve +.IP "chomp \s-1VARIABLE\s0" 8 +.IX Xref "chomp INPUT_RECORD_SEPARATOR $ newline eol" +.IX Item "chomp VARIABLE" +.PD 0 +.IP "chomp( \s-1LIST\s0 )" 8 +.IX Item "chomp( LIST )" +.IP "chomp" 8 +.IX Item "chomp" +.PD +This safer version of \*(L"chop\*(R" removes any trailing string +that corresponds to the current value of \f(CW$/\fR (also known as +\&\f(CW$INPUT_RECORD_SEPARATOR\fR in the \f(CW\*(C`English\*(C'\fR module). It returns the total +number of characters removed from all its arguments. It's often used to +remove the newline from the end of an input record when you're worried +that the final record may be missing its newline. When in paragraph +mode (\f(CW\*(C`$/ = ""\*(C'\fR), it removes all trailing newlines from the string. +When in slurp mode (\f(CW\*(C`$/ = undef\*(C'\fR) or fixed-length record mode (\f(CW$/\fR is +a reference to an integer or the like, see perlvar) \fIchomp()\fR won't +remove anything. +If \s-1VARIABLE\s0 is omitted, it chomps \f(CW$_\fR. Example: +.Sp +.Vb 5 +\& while (<>) { +\& chomp; # avoid \en on last field +\& @array = split(/:/); +\& # ... +\& } +.Ve +.Sp +If \s-1VARIABLE\s0 is a hash, it chomps the hash's values, but not its keys. +.Sp + diff --git a/tests/examplefiles/phpcomplete.vim b/tests/examplefiles/phpcomplete.vim new file mode 100644 index 0000000..17d74fd --- /dev/null +++ b/tests/examplefiles/phpcomplete.vim @@ -0,0 +1,567 @@ +" Vim completion script +" Language: PHP +" Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl ) +" Last Change: 2006 May 9 +" +" TODO: +" - Class aware completion: +" a) caching? +" - Switching to HTML (XML?) completion (SQL) inside of phpStrings +" - allow also for XML completion <- better do html_flavor for HTML +" completion +" - outside of getting parent tag may cause problems. Heh, even in +" perfect conditions GetLastOpenTag doesn't cooperate... Inside of +" phpStrings this can be even a bonus but outside of it is not the +" best situation + +function! phpcomplete#CompletePHP(findstart, base) + if a:findstart + unlet! b:php_menu + " Check if we are inside of PHP markup + let pos = getpos('.') + let phpbegin = searchpairpos('', 'bWn', + \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\|comment"') + let phpend = searchpairpos('', 'Wn', + \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\|comment"') + + if phpbegin == [0,0] && phpend == [0,0] + " We are outside of any PHP markup. Complete HTML + let htmlbegin = htmlcomplete#CompleteTags(1, '') + let cursor_col = pos[2] + let base = getline('.')[htmlbegin : cursor_col] + let b:php_menu = htmlcomplete#CompleteTags(0, base) + return htmlbegin + else + " locate the start of the word + let line = getline('.') + let start = col('.') - 1 + let curline = line('.') + let compl_begin = col('.') - 2 + while start >= 0 && line[start - 1] =~ '[a-zA-Z_0-9\x7f-\xff$]' + let start -= 1 + endwhile + let b:compl_context = getline('.')[0:compl_begin] + return start + + " We can be also inside of phpString with HTML tags. Deal with + " it later (time, not lines). + endif + + endif + " If exists b:php_menu it means completion was already constructed we + " don't need to do anything more + if exists("b:php_menu") + return b:php_menu + endif + " Initialize base return lists + let res = [] + let res2 = [] + " a:base is very short - we need context + if exists("b:compl_context") + let context = b:compl_context + unlet! b:compl_context + endif + + if !exists('g:php_builtin_functions') + call phpcomplete#LoadData() + endif + + let scontext = substitute(context, '\$\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*$', '', '') + + if scontext =~ '\(=\s*new\|extends\)\s\+$' + " Complete class name + " Internal solution for finding classes in current file. + let file = getline(1, '$') + call filter(file, + \ 'v:val =~ "class\\s\\+[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*("') + let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")')) + let jfile = join(file, ' ') + let int_values = split(jfile, 'class\s\+') + let int_classes = {} + for i in int_values + let c_name = matchstr(i, '^[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*') + if c_name != '' + let int_classes[c_name] = '' + endif + endfor + + " Prepare list of classes from tags file + let ext_classes = {} + let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")')) + if fnames != '' + exe 'silent! vimgrep /^'.a:base.'.*\tc\(\t\|$\)/j '.fnames + let qflist = getqflist() + if len(qflist) > 0 + for field in qflist + " [:space:] thing: we don't have to be so strict when + " dealing with tags files - entries there were already + " checked by ctags. + let item = matchstr(field['text'], '^[^[:space:]]\+') + let ext_classes[item] = '' + endfor + endif + endif + + " Prepare list of built in classes from g:php_builtin_functions + if !exists("g:php_omni_bi_classes") + let g:php_omni_bi_classes = {} + for i in keys(g:php_builtin_object_functions) + let g:php_omni_bi_classes[substitute(i, '::.*$', '', '')] = '' + endfor + endif + + let classes = sort(keys(int_classes)) + let classes += sort(keys(ext_classes)) + let classes += sort(keys(g:php_omni_bi_classes)) + + for m in classes + if m =~ '^'.a:base + call add(res, m) + endif + endfor + + let final_menu = [] + for i in res + let final_menu += [{'word':i, 'kind':'c'}] + endfor + + return final_menu + + elseif scontext =~ '\(->\|::\)$' + " Complete user functions and variables + " Internal solution for current file. + " That seems as unnecessary repeating of functions but there are + " few not so subtle differences as not appending of $ and addition + " of 'kind' tag (not necessary in regular completion) + + if scontext =~ '->$' && scontext !~ '\$this->$' + + " Get name of the class + let classname = phpcomplete#GetClassName(scontext) + + " Get location of class definition, we have to iterate through all + " tags files separately because we need relative path from current + " file to the exact file (tags file can be in different dir) + if classname != '' + let classlocation = phpcomplete#GetClassLocation(classname) + else + let classlocation = '' + endif + + if classlocation == 'VIMPHP_BUILTINOBJECT' + + for object in keys(g:php_builtin_object_functions) + if object =~ '^'.classname + let res += [{'word':substitute(object, '.*::', '', ''), + \ 'info': g:php_builtin_object_functions[object]}] + endif + endfor + + return res + + endif + + if filereadable(classlocation) + let classfile = readfile(classlocation) + let classcontent = '' + let classcontent .= "\n".phpcomplete#GetClassContents(classfile, classname) + let sccontent = split(classcontent, "\n") + + " YES, YES, YES! - we have whole content including extends! + " Now we need to get two elements: public functions and public + " vars + " NO, NO, NO! - third separate filtering looking for content + " :(, but all of them have differences. To squeeze them into + " one implementation would require many additional arguments + " and ifs. No good solution + " Functions declared with public keyword or without any + " keyword are public + let functions = filter(deepcopy(sccontent), + \ 'v:val =~ "^\\s*\\(static\\s\\+\\|public\\s\\+\\)*function"') + let jfuncs = join(functions, ' ') + let sfuncs = split(jfuncs, 'function\s\+') + let c_functions = {} + for i in sfuncs + let f_name = matchstr(i, + \ '^&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze') + let f_args = matchstr(i, + \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*{') + if f_name != '' + let c_functions[f_name.'('] = f_args + endif + endfor + " Variables declared with var or with public keyword are + " public + let variables = filter(deepcopy(sccontent), + \ 'v:val =~ "^\\s*\\(public\\|var\\)\\s\\+\\$"') + let jvars = join(variables, ' ') + let svars = split(jvars, '\$') + let c_variables = {} + for i in svars + let c_var = matchstr(i, + \ '^\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze') + if c_var != '' + let c_variables[c_var] = '' + endif + endfor + + let all_values = {} + call extend(all_values, c_functions) + call extend(all_values, c_variables) + + for m in sort(keys(all_values)) + if m =~ '^'.a:base && m !~ '::' + call add(res, m) + elseif m =~ '::'.a:base + call add(res2, m) + endif + endfor + + let start_list = res + res2 + + let final_list = [] + for i in start_list + if has_key(c_variables, i) + let class = ' ' + if all_values[i] != '' + let class = i.' class ' + endif + let final_list += + \ [{'word':i, + \ 'info':class.all_values[i], + \ 'kind':'v'}] + else + let final_list += + \ [{'word':substitute(i, '.*::', '', ''), + \ 'info':i.all_values[i].')', + \ 'kind':'f'}] + endif + endfor + + return final_list + + endif + + endif + + if a:base =~ '^\$' + let adddollar = '$' + else + let adddollar = '' + endif + let file = getline(1, '$') + let jfile = join(file, ' ') + let sfile = split(jfile, '\$') + let int_vars = {} + for i in sfile + if i =~ '^\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*=\s*new' + let val = matchstr(i, '^[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*').'->' + else + let val = matchstr(i, '^[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*') + endif + if val !~ '' + let int_vars[adddollar.val] = '' + endif + endfor + + " ctags has good support for PHP, use tags file for external + " variables + let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")')) + let ext_vars = {} + if fnames != '' + let sbase = substitute(a:base, '^\$', '', '') + exe 'silent! vimgrep /^'.sbase.'.*\tv\(\t\|$\)/j '.fnames + let qflist = getqflist() + if len(qflist) > 0 + for field in qflist + let item = matchstr(field['text'], '^[^[:space:]]\+') + " Add -> if it is possible object declaration + let classname = '' + if field['text'] =~ item.'\s*=\s*new\s\+' + let item = item.'->' + let classname = matchstr(field['text'], + \ '=\s*new\s\+\zs[a-zA-Z_0-9\x7f-\xff]\+\ze') + endif + let ext_vars[adddollar.item] = classname + endfor + endif + endif + + " Now we have all variables in int_vars dictionary + call extend(int_vars, ext_vars) + + " Internal solution for finding functions in current file. + let file = getline(1, '$') + call filter(file, + \ 'v:val =~ "function\\s\\+&\\?[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*("') + let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")')) + let jfile = join(file, ' ') + let int_values = split(jfile, 'function\s\+') + let int_functions = {} + for i in int_values + let f_name = matchstr(i, + \ '^&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze') + let f_args = matchstr(i, + \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*{') + let int_functions[f_name.'('] = f_args.')' + endfor + + " Prepare list of functions from tags file + let ext_functions = {} + if fnames != '' + exe 'silent! vimgrep /^'.a:base.'.*\tf\(\t\|$\)/j '.fnames + let qflist = getqflist() + if len(qflist) > 0 + for field in qflist + " File name + let item = matchstr(field['text'], '^[^[:space:]]\+') + let fname = matchstr(field['text'], '\t\zs\f\+\ze') + let prototype = matchstr(field['text'], + \ 'function\s\+&\?[^[:space:]]\+\s*(\s*\zs.\{-}\ze\s*)\s*{\?') + let ext_functions[item.'('] = prototype.') - '.fname + endfor + endif + endif + + let all_values = {} + call extend(all_values, int_functions) + call extend(all_values, ext_functions) + call extend(all_values, int_vars) " external variables are already in + call extend(all_values, g:php_builtin_object_functions) + + for m in sort(keys(all_values)) + if m =~ '\(^\|::\)'.a:base + call add(res, m) + endif + endfor + + let start_list = res + + let final_list = [] + for i in start_list + if has_key(int_vars, i) + let class = ' ' + if all_values[i] != '' + let class = i.' class ' + endif + let final_list += [{'word':i, 'info':class.all_values[i], 'kind':'v'}] + else + let final_list += + \ [{'word':substitute(i, '.*::', '', ''), + \ 'info':i.all_values[i], + \ 'kind':'f'}] + endif + endfor + + return final_list + endif + + if a:base =~ '^\$' + " Complete variables + " Built-in variables {{{ + let g:php_builtin_vars = {'$GLOBALS':'', + \ '$_SERVER':'', + \ '$_GET':'', + \ '$_POST':'', + \ '$_COOKIE':'', + \ '$_FILES':'', + \ '$_ENV':'', + \ '$_REQUEST':'', + \ '$_SESSION':'', + \ '$HTTP_SERVER_VARS':'', + \ '$HTTP_ENV_VARS':'', + \ '$HTTP_COOKIE_VARS':'', + \ '$HTTP_GET_VARS':'', + \ '$HTTP_POST_VARS':'', + \ '$HTTP_POST_FILES':'', + \ '$HTTP_SESSION_VARS':'', + \ '$php_errormsg':'', + \ '$this':'' + \ } + " }}} + + " Internal solution for current file. + let file = getline(1, '$') + let jfile = join(file, ' ') + let int_vals = split(jfile, '\ze\$') + let int_vars = {} + for i in int_vals + if i =~ '^\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*=\s*new' + let val = matchstr(i, + \ '^\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*').'->' + else + let val = matchstr(i, + \ '^\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*') + endif + if val != '' + let int_vars[val] = '' + endif + endfor + + call extend(int_vars,g:php_builtin_vars) + + " ctags has support for PHP, use tags file for external variables + let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")')) + let ext_vars = {} + if fnames != '' + let sbase = substitute(a:base, '^\$', '', '') + exe 'silent! vimgrep /^'.sbase.'.*\tv\(\t\|$\)/j '.fnames + let qflist = getqflist() + if len(qflist) > 0 + for field in qflist + let item = '$'.matchstr(field['text'], '^[^[:space:]]\+') + let m_menu = '' + " Add -> if it is possible object declaration + if field['text'] =~ item.'\s*=\s*new\s\+' + let item = item.'->' + let m_menu = matchstr(field['text'], + \ '=\s*new\s\+\zs[a-zA-Z_0-9\x7f-\xff]\+\ze') + endif + let ext_vars[item] = m_menu + endfor + endif + endif + + call extend(int_vars, ext_vars) + let g:a0 = keys(int_vars) + + for m in sort(keys(int_vars)) + if m =~ '^\'.a:base + call add(res, m) + endif + endfor + + let int_list = res + + let int_dict = [] + for i in int_list + if int_vars[i] != '' + let class = ' ' + if int_vars[i] != '' + let class = i.' class ' + endif + let int_dict += [{'word':i, 'info':class.int_vars[i], 'kind':'v'}] + else + let int_dict += [{'word':i, 'kind':'v'}] + endif + endfor + + return int_dict + + else + " Complete everything else - + " + functions, DONE + " + keywords of language DONE + " + defines (constant definitions), DONE + " + extend keywords for predefined constants, DONE + " + classes (after new), DONE + " + limit choice after -> and :: to funcs and vars DONE + + " Internal solution for finding functions in current file. + let file = getline(1, '$') + call filter(file, + \ 'v:val =~ "function\\s\\+&\\?[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*("') + let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")')) + let jfile = join(file, ' ') + let int_values = split(jfile, 'function\s\+') + let int_functions = {} + for i in int_values + let f_name = matchstr(i, + \ '^&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze') + let f_args = matchstr(i, + \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\s*\zs.\{-}\ze\s*)\_s*{') + let int_functions[f_name.'('] = f_args.')' + endfor + + " Prepare list of functions from tags file + let ext_functions = {} + if fnames != '' + exe 'silent! vimgrep /^'.a:base.'.*\tf\(\t\|$\)/j '.fnames + let qflist = getqflist() + if len(qflist) > 0 + for field in qflist + " File name + let item = matchstr(field['text'], '^[^[:space:]]\+') + let fname = matchstr(field['text'], '\t\zs\f\+\ze') + let prototype = matchstr(field['text'], + \ 'function\s\+&\?[^[:space:]]\+\s*(\s*\zs.\{-}\ze\s*)\s*{\?') + let ext_functions[item.'('] = prototype.') - '.fname + endfor + endif + endif + + " All functions + call extend(int_functions, ext_functions) + call extend(int_functions, g:php_builtin_functions) + + " Internal solution for finding constants in current file + let file = getline(1, '$') + call filter(file, 'v:val =~ "define\\s*("') + let jfile = join(file, ' ') + let int_values = split(jfile, 'define\s*(\s*') + let int_constants = {} + for i in int_values + let c_name = matchstr(i, '\(["'']\)\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze\1') + " let c_value = matchstr(i, + " \ '\(["'']\)[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\1\s*,\s*\zs.\{-}\ze\s*)') + if c_name != '' + let int_constants[c_name] = '' " c_value + endif + endfor + + " Prepare list of constants from tags file + let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")')) + let ext_constants = {} + if fnames != '' + exe 'silent! vimgrep /^'.a:base.'.*\td\(\t\|$\)/j '.fnames + let qflist = getqflist() + if len(qflist) > 0 + for field in qflist + let item = matchstr(field['text'], '^[^[:space:]]\+') + let ext_constants[item] = '' + endfor + endif + endif + + " All constants + call extend(int_constants, ext_constants) + " Treat keywords as constants + + let all_values = {} + + " One big dictionary of functions + call extend(all_values, int_functions) + + " Add constants + call extend(all_values, int_constants) + " Add keywords + call extend(all_values, g:php_keywords) + + for m in sort(keys(all_values)) + if m =~ '^'.a:base + call add(res, m) + endif + endfor + + let int_list = res + + let final_list = [] + for i in int_list + if has_key(int_functions, i) + let final_list += + \ [{'word':i, + \ 'info':i.int_functions[i], + \ 'kind':'f'}] + elseif has_key(int_constants, i) + let final_list += [{'word':i, 'kind':'d'}] + else + let final_list += [{'word':i}] + endif + endfor + + return final_list + + endif + +endfunction +" vim:set foldmethod=marker: diff --git a/tests/examplefiles/pleac.in.rb b/tests/examplefiles/pleac.in.rb new file mode 100644 index 0000000..d1dea9f --- /dev/null +++ b/tests/examplefiles/pleac.in.rb @@ -0,0 +1,1223 @@ +# -*- ruby -*- + +# Local variables: +# indent-tabs-mode: nil +# ruby-indent-level: 4 +# End: + +# @@PLEAC@@_NAME +# @@SKIP@@ Ruby + +# @@PLEAC@@_WEB +# @@SKIP@@ http://www.ruby-lang.org + + +# @@PLEAC@@_1.0 +string = '\n' # two characters, \ and an n +string = 'Jon \'Maddog\' Orwant' # literal single quotes + +string = "\n" # a "newline" character +string = "Jon \"Maddog\" Orwant" # literal double quotes + +string = %q/Jon 'Maddog' Orwant/ # literal single quotes + +string = %q[Jon 'Maddog' Orwant] # literal single quotes +string = %q{Jon 'Maddog' Orwant} # literal single quotes +string = %q(Jon 'Maddog' Orwant) # literal single quotes +string = %q # literal single quotes + +a = <<"EOF" +This is a multiline here document +terminated by EOF on a line by itself +EOF + + +# @@PLEAC@@_1.1 +value = string[offset,count] +value = string[offset..-1] + +string[offset,count] = newstring +string[offset..-1] = newtail + +# in Ruby we can also specify intervals by their two offsets +value = string[offset..offs2] +string[offset..offs2] = newstring + +leading, s1, s2, trailing = data.unpack("A5 x3 A8 A8 A*") + +fivers = string.unpack("A5" * (string.length/5)) + +chars = string.unpack("A1" * string.length) + +string = "This is what you have" +# +012345678901234567890 Indexing forwards (left to right) +# 109876543210987654321- Indexing backwards (right to left) +# note that 0 means 10 or 20, etc. above + +first = string[0, 1] # "T" +start = string[5, 2] # "is" +rest = string[13..-1] # "you have" +last = string[-1, 1] # "e" +end_ = string[-4..-1] # "have" +piece = string[-8, 3] # "you" + +string[5, 2] = "wasn't" # change "is" to "wasn't" +string[-12..-1] = "ondrous" # "This wasn't wondrous" +string[0, 1] = "" # delete first character +string[-10..-1] = "" # delete last 10 characters + +if string[-10..-1] =~ /pattern/ + puts "Pattern matches in last 10 characters" +end + +string[0, 5].gsub!(/is/, 'at') + +a = "make a hat" +a[0, 1], a[-1, 1] = a[-1, 1], a[0, 1] + +a = "To be or not to be" +b = a.unpack("x6 A6") + +b, c = a.unpack("x6 A2 X5 A2") +puts "#{b}\n#{c}\n" + +def cut2fmt(*args) + template = '' + lastpos = 1 + for place in args + template += "A" + (place - lastpos).to_s + " " + lastpos = place + end + template += "A*" + return template +end + +fmt = cut2fmt(8, 14, 20, 26, 30) + + +# @@PLEAC@@_1.2 +# careful! "b is true" doesn't mean "b != 0" (0 is true in Ruby) +# thus no problem of "defined" later since only nil is false +# the following sets to `c' if `b' is nil or false +a = b || c + +# if you need Perl's behaviour (setting to `c' if `b' is 0) the most +# effective way is to use Numeric#nonzero? (thanks to Dave Thomas!) +a = b.nonzero? || c + +# you will still want to use defined? in order to test +# for scope existence of a given object +a = defined?(b) ? b : c + +dir = ARGV.shift || "/tmp" + + +# @@PLEAC@@_1.3 +v1, v2 = v2, v1 + +alpha, beta, production = %w(January March August) +alpha, beta, production = beta, production, alpha + + +# @@PLEAC@@_1.4 +num = char[0] +char = num.chr + +# Ruby also supports having a char from character constant +num = ?r + +char = sprintf("%c", num) +printf("Number %d is character %c\n", num, num) + +ascii = string.unpack("C*") +string = ascii.pack("C*") + +hal = "HAL" +ascii = hal.unpack("C*") +# We can't use Array#each since we can't mutate a Fixnum +ascii.collect! { |i| + i + 1 # add one to each ASCII value +} +ibm = ascii.pack("C*") +puts ibm + + +# @@PLEAC@@_1.5 +array = string.split('') + +array = string.unpack("C*") + +string.scan(/./) { |b| + # do something with b +} + +string = "an apple a day" +print "unique chars are: ", string.split('').uniq.sort, "\n" + +sum = 0 +for ascval in string.unpack("C*") # or use Array#each for a pure OO style :) + sum += ascval +end +puts "sum is #{sum & 0xffffffff}" # since Ruby will go Bignum if necessary + +# @@INCLUDE@@ include/ruby/slowcat.rb + + +# @@PLEAC@@_1.6 +revbytes = string.reverse + +revwords = string.split(" ").reverse.join(" ") + +revwords = string.split(/(\s+)/).reverse.join + +# using the fact that IO is Enumerable, you can directly "select" it +long_palindromes = File.open("/usr/share/dict/words"). + select { |w| w.chomp!; w.reverse == w && w.length > 5 } + + +# @@PLEAC@@_1.7 +while string.sub!("\t+") { ' ' * ($&.length * 8 - $`.length % 8) } +end + + +# @@PLEAC@@_1.8 +'You owe #{debt} to me'.gsub(/\#{(\w+)}/) { eval($1) } + +rows, cols = 24, 80 +text = %q(I am #{rows} high and #{cols} long) +text.gsub!(/\#{(\w+)}/) { eval("#{$1}") } +puts text + +'I am 17 years old'.gsub(/\d+/) { 2 * $&.to_i } + + +# @@PLEAC@@_1.9 +e = "bo peep".upcase +e.downcase! +e.capitalize! + +"thIS is a loNG liNE".gsub!(/\w+/) { $&.capitalize } + + +# @@PLEAC@@_1.10 +"I have #{n+1} guanacos." +print "I have ", n+1, " guanacos." + + +# @@PLEAC@@_1.11 +var = <<'EOF'.gsub(/^\s+/, '') + your text + goes here +EOF + + +# @@PLEAC@@_1.12 +string = "Folding and splicing is the work of an editor,\n"+ + "not a mere collection of silicon\n"+ + "and\n"+ + "mobile electrons!" + +def wrap(str, max_size) + all = [] + line = '' + for l in str.split + if (line+l).length >= max_size + all.push(line) + line = '' + end + line += line == '' ? l : ' ' + l + end + all.push(line).join("\n") +end + +print wrap(string, 20) +#=> Folding and +#=> splicing is the +#=> work of an editor, +#=> not a mere +#=> collection of +#=> silicon and mobile +#=> electrons! + + +# @@PLEAC@@_1.13 +string = %q(Mom said, "Don't do that.") +string.gsub(/['"]/) { '\\'+$& } +string.gsub(/['"]/, '\&\&') +string.gsub(/[^A-Z]/) { '\\'+$& } +"is a test!".gsub(/\W/) { '\\'+$& } # no function like quotemeta? + + +# @@PLEAC@@_1.14 +string.strip! + + +# @@PLEAC@@_1.15 +def parse_csv(text) + new = text.scan(/"([^\"\\]*(?:\\.[^\"\\]*)*)",?|([^,]+),?|,/) + new << nil if text[-1] == ?, + new.flatten.compact +end + +line = %q +fields = parse_csv(line) +fields.each_with_index { |v,i| + print "#{i} : #{v}\n"; +} + + +# @@PLEAC@@_1.16 +# Use the soundex.rb Library from Michael Neumann. +# http://www.s-direktnet.de/homepages/neumann/rb_prgs/Soundex.rb +require 'Soundex' + +code = Text::Soundex.soundex(string) +codes = Text::Soundex.soundex(array) + +# substitution function for getpwent(): +# returns an array of user entries, +# each entry contains the username and the full name +def login_names + result = [] + File.open("/etc/passwd") { |file| + file.each_line { |line| + next if line.match(/^#/) + cols = line.split(":") + result.push([cols[0], cols[4]]) + } + } + result +end + +puts "Lookup user: " +user = STDIN.gets +user.chomp! +exit unless user +name_code = Text::Soundex.soundex(user) + +splitter = Regexp.new('(\w+)[^,]*\b(\w+)') +for username, fullname in login_names do + firstname, lastname = splitter.match(fullname)[1,2] + if name_code == Text::Soundex.soundex(username) + || name_code == Text::Soundex.soundex(firstname) + || name_code == Text::Soundex.soundex(lastname) + then + puts "#{username}: #{firstname} #{lastname}" + end +end + + +# @@PLEAC@@_1.17 +# @@INCLUDE@@ include/ruby/fixstyle.rb + + +# @@PLEAC@@_1.18 +# @@INCLUDE@@ include/ruby/psgrep.rb + + +# @@PLEAC@@_2.1 +# Matz tells that you can use Integer() for strict checked conversion. +Integer("abc") +#=> `Integer': invalid value for Integer: "abc" (ArgumentError) +Integer("567") +#=> 567 + +# You may use Float() for floating point stuff +Integer("56.7") +#=> `Integer': invalid value for Integer: "56.7" (ArgumentError) +Float("56.7") +#=> 56.7 + +# You may also use a regexp for that +if string =~ /^[+-]?\d+$/ + p 'is an integer' +else + p 'is not' +end + +if string =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/ + p 'is a decimal number' +else + p 'is not' +end + + +# @@PLEAC@@_2.2 +# equal(num1, num2, accuracy) : returns true if num1 and num2 are +# equal to accuracy number of decimal places +def equal(i, j, a) + sprintf("%.#{a}g", i) == sprintf("%.#{a}g", j) +end + +wage = 536 # $5.36/hour +week = 40 * wage # $214.40 +printf("One week's wage is: \$%.2f\n", week/100.0) + + +# @@PLEAC@@_2.3 +num.round # rounds to integer + +a = 0.255 +b = sprintf("%.2f", a) +print "Unrounded: #{a}\nRounded: #{b}\n" +printf "Unrounded: #{a}\nRounded: %.2f\n", a + +print "number\tint\tfloor\tceil\n" +a = [ 3.3 , 3.5 , 3.7, -3.3 ] +for n in a + printf("% .1f\t% .1f\t% .1f\t% .1f\n", # at least I don't fake my output :) + n, n.to_i, n.floor, n.ceil) +end + + +# @@PLEAC@@_2.4 +def dec2bin(n) + [n].pack("N").unpack("B32")[0].sub(/^0+(?=\d)/, '') +end + +def bin2dec(n) + [("0"*32+n.to_s)[-32..-1]].pack("B32").unpack("N")[0] +end + + +# @@PLEAC@@_2.5 +for i in x .. y + # i is set to every integer from x to y, inclusive +end + +x.step(y,7) { |i| + # i is set to every integer from x to y, stepsize = 7 +} + +print "Infancy is: " +(0..2).each { |i| + print i, " " +} +print "\n" + + +# @@PLEAC@@_2.6 +# We can add conversion methods to the Integer class, +# this makes a roman number just a representation for normal numbers. +class Integer + + @@romanlist = [["M", 1000], + ["CM", 900], + ["D", 500], + ["CD", 400], + ["C", 100], + ["XC", 90], + ["L", 50], + ["XL", 40], + ["X", 10], + ["IX", 9], + ["V", 5], + ["IV", 4], + ["I", 1]] + + def to_roman + remains = self + roman = "" + for sym, num in @@romanlist + while remains >= num + remains -= num + roman << sym + end + end + roman + end + + def Integer.from_roman(roman) + ustr = roman.upcase + sum = 0 + for entry in @@romanlist + sym, num = entry[0], entry[1] + while sym == ustr[0, sym.length] + sum += num + ustr.slice!(0, sym.length) + end + end + sum + end + +end + + +roman_fifteen = 15.to_roman +puts "Roman for fifteen is #{roman_fifteen}" +i = Integer.from_roman(roman_fifteen) +puts "Converted back, #{roman_fifteen} is #{i}" + +# check +for i in (1..3900) + r = i.to_roman + j = Integer.from_roman(r) + if i != j + puts "error: #{i} : #{r} - #{j}" + end +end + + +# @@PLEAC@@_2.7 +random = rand(y-x+1)+x + +chars = ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!@$%^&*) +password = (1..8).collect { chars[rand(chars.size)] }.pack("C*") + + +# @@PLEAC@@_2.8 +srand # uses a combination of the time, the process id, and a sequence number +srand(val) # for repeatable behaviour + + +# @@PLEAC@@_2.9 +# from the randomr lib: +# http://raa.ruby-lang.org/project/randomr/ +----> http://raa.ruby-lang.org/project/randomr/ + +require 'random/mersenne_twister' +mers = Random::MersenneTwister.new 123456789 +puts mers.rand(0) # 0.550321932544541 +puts mers.rand(10) # 2 + +# using online sources of random data via the realrand package: +# http://raa.ruby-lang.org/project/realrand/ +# **Note** +# The following online services are used in this package: +# http://www.random.org - source: atmospheric noise +# http://www.fourmilab.ch/hotbits - source: radioactive decay timings +# http://random.hd.org - source: entropy from local and network noise +# Please visit the sites and respect the rules of each service. + +require 'random/online' + +generator1 = Random::RandomOrg.new +puts generator1.randbyte(5).join(",") +puts generator1.randnum(10, 1, 6).join(",") # Roll dice 10 times. + +generator2 = Random::FourmiLab.new +puts generator2.randbyte(5).join(",") +# randnum is not supported. + +generator3 = Random::EntropyPool.new +puts generator3.randbyte(5).join(",") +# randnum is not supported. + + +# @@PLEAC@@_2.10 +def gaussian_rand + begin + u1 = 2 * rand() - 1 + u2 = 2 * rand() - 1 + w = u1*u1 + u2*u2 + end while (w >= 1) + w = Math.sqrt((-2*Math.log(w))/w) + [ u2*w, u1*w ] +end + +mean = 25 +sdev = 2 +salary = gaussian_rand[0] * sdev + mean +printf("You have been hired at \$%.2f\n", salary) + + +# @@PLEAC@@_2.11 +def deg2rad(d) + (d/180.0)*Math::PI +end + +def rad2deg(r) + (r/Math::PI)*180 +end + + +# @@PLEAC@@_2.12 +sin_val = Math.sin(angle) +cos_val = Math.cos(angle) +tan_val = Math.tan(angle) + +# AFAIK Ruby's Math module doesn't provide acos/asin +# While we're at it, let's also define missing hyperbolic functions +module Math + def Math.asin(x) + atan2(x, sqrt(1 - x**2)) + end + def Math.acos(x) + atan2(sqrt(1 - x**2), x) + end + def Math.atan(x) + atan2(x, 1) + end + def Math.sinh(x) + (exp(x) - exp(-x)) / 2 + end + def Math.cosh(x) + (exp(x) + exp(-x)) / 2 + end + def Math.tanh(x) + sinh(x) / cosh(x) + end +end + +# The support for Complex numbers is not built-in +y = Math.acos(3.7) +#=> in `sqrt': square root for negative number (ArgumentError) + +# There is an implementation of Complex numbers in 'complex.rb' in current +# Ruby distro, but it doesn't support atan2 with complex args, so it doesn't +# solve this problem. + + +# @@PLEAC@@_2.13 +log_e = Math.log(val) +log_10 = Math.log10(val) + +def log_base(base, val) + Math.log(val)/Math.log(base) +end + +answer = log_base(10, 10_000) +puts "log10(10,000) = #{answer}" + + +# @@PLEAC@@_2.14 +require 'matrix.rb' + +a = Matrix[[3, 2, 3], [5, 9, 8]] +b = Matrix[[4, 7], [9, 3], [8, 1]] +c = a * b + +a.row_size +a.column_size + +c.det +a.transpose + + +# @@PLEAC@@_2.15 +require 'complex.rb' +require 'rational.rb' + +a = Complex(3, 5) # 3 + 5i +b = Complex(2, -2) # 2 - 2i +puts "c = #{a*b}" + +c = a * b +d = 3 + 4*Complex::I + +printf "sqrt(#{d}) = %s\n", Math.sqrt(d) + + +# @@PLEAC@@_2.16 +number = hexadecimal.hex +number = octal.oct + +print "Gimme a number in decimal, octal, or hex: " +num = gets.chomp +exit unless defined?(num) +num = num.oct if num =~ /^0/ # does both oct and hex +printf "%d %x %o\n", num, num, num + +print "Enter file permission in octal: " +permissions = gets.chomp +raise "Exiting ...\n" unless defined?(permissions) +puts "The decimal value is #{permissions.oct}" + + +# @@PLEAC@@_2.17 +def commify(n) + n.to_s =~ /([^\.]*)(\..*)?/ + int, dec = $1.reverse, $2 ? $2 : "" + while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2,\3') + end + int.reverse + dec +end + + +# @@PLEAC@@_2.18 +printf "It took %d hour%s\n", time, time == 1 ? "" : "s" + +# dunno if an equivalent to Lingua::EN::Inflect exists... + + +# @@PLEAC@@_2.19 +#----------------------------- +#!/usr/bin/ruby +# bigfact - calculating prime factors +def factorize(orig) + factors = {} + factors.default = 0 # return 0 instead nil if key not found in hash + n = orig + i = 2 + sqi = 4 # square of i + while sqi <= n do + while n.modulo(i) == 0 do + n /= i + factors[i] += 1 + # puts "Found factor #{i}" + end + # we take advantage of the fact that (i +1)**2 = i**2 + 2*i +1 + sqi += 2 * i + 1 + i += 1 + end + + if (n != 1) && (n != orig) + factors[n] += 1 + end + factors +end + +def printfactorhash(orig, factorcount) + print format("%-10d ", orig) + if factorcount.length == 0 + print "PRIME" + else + # sorts after number, because the hash keys are numbers + factorcount.sort.each { |factor,exponent| + print factor + if exponent > 1 + print "**", exponent + end + print " " + } + end + puts +end + +for arg in ARGV + n = arg.to_i + mfactors = factorize(n) + printfactorhash(n, mfactors) +end +#----------------------------- + + +# @@PLEAC@@_3.0 +puts Time.now + +print "Today is day ", Time.now.yday, " of the current year.\n" +print "Today is day ", Time.now.day, " of the current month.\n" + + +# @@PLEAC@@_3.1 +day, month, year = Time.now.day, Time.now.month, Time.now.year +# or +day, month, year = Time.now.to_a[3..5] + +tl = Time.now.localtime +printf("The current date is %04d %02d %02d\n", tl.year, tl.month, tl.day) + +Time.now.localtime.strftime("%Y-%m-%d") + + +# @@PLEAC@@_3.2 +Time.local(year, month, day, hour, minute, second).tv_sec +Time.gm(year, month, day, hour, minute, second).tv_sec + + +# @@PLEAC@@_3.3 +sec, min, hour, day, month, year, wday, yday, isdst, zone = Time.at(epoch_secs).to_a + + +# @@PLEAC@@_3.4 +when_ = now + difference # now -> Time ; difference -> Numeric (delta in seconds) +then_ = now - difference + + +# @@PLEAC@@_3.5 +bree = 361535725 +nat = 96201950 + +difference = bree - nat +puts "There were #{difference} seconds between Nat and Bree" + +seconds = difference % 60 +difference = (difference - seconds) / 60 +minutes = difference % 60 +difference = (difference - minutes) / 60 +hours = difference % 24 +difference = (difference - hours) / 24 +days = difference % 7 +weeks = (difference - days) / 7 + +puts "(#{weeks} weeks, #{days} days, #{hours}:#{minutes}:#{seconds})" + + +# @@PLEAC@@_3.6 +monthday, weekday, yearday = date.mday, date.wday, date.yday + +# AFAIK the week number is not just a division since week boundaries are on sundays +weeknum = d.strftime("%U").to_i + 1 + +year = 1981 +month = "jun" # or `6' if you want to emulate a broken language +day = 16 +t = Time.mktime(year, month, day) +print "#{month}/#{day}/#{year} was a ", t.strftime("%A"), "\n" + + +# @@PLEAC@@_3.7 +yyyy, mm, dd = $1, $2, $3 if "1998-06-25" =~ /(\d+)-(\d+)-(\d+)/ + +epoch_seconds = Time.mktime(yyyy, mm, dd).tv_sec + +# dunno an equivalent to Date::Manip#ParseDate + + +# @@PLEAC@@_3.8 +string = Time.at(epoch_secs) +Time.at(1234567890).gmtime # gives: Fri Feb 13 23:31:30 UTC 2009 + +time = Time.mktime(1973, "jan", 18, 3, 45, 50) +print "In localtime it gives: ", time.localtime, "\n" + + +# @@PLEAC@@_3.9 +# Ruby provides micro-seconds in Time object +Time.now.usec + +# Ruby gives the seconds in floating format when substracting two Time objects +before = Time.now +line = gets +elapsed = Time.now - before +puts "You took #{elapsed} seconds." + +# On my Celeron-400 with Linux-2.2.19-14mdk, average for three execs are: +# This Ruby version: average 0.00321 sec +# Cookbook's Perl version: average 0.00981 sec +size = 500 +number_of_times = 100 +total_time = 0 +number_of_times.times { + # populate array + array = [] + size.times { array << rand } + # sort it + begin_ = Time.now + array.sort! + time = Time.now - begin_ + total_time += time +} +printf "On average, sorting %d random numbers takes %.5f seconds\n", + size, (total_time/Float(number_of_times)) + + +# @@PLEAC@@_3.10 +sleep(0.005) # Ruby is definitely not as broken as Perl :) +# (may be interrupted by sending the process a SIGALRM) + + +# @@PLEAC@@_3.11 +#!/usr/bin/ruby -w +# hopdelta - feed mail header, produce lines +# showing delay at each hop. +require 'time' +class MailHopDelta + + def initialize(mail) + @head = mail.gsub(/\n\s+/,' ') + @topline = %w-Sender Recipient Time Delta- + @start_from = mail.match(/^From.*\@([^\s>]*)/)[1] + @date = Time.parse(mail.match(/^Date:\s+(.*)/)[1]) + end + + def out(line) + "%-20.20s %-20.20s %-20.20s %s" % line + end + + def hop_date(day) + day.strftime("%I:%M:%S %Y/%m/%d") + end + + def puts_hops + puts out(@topline) + puts out(['Start', @start_from, hop_date(@date),'']) + @head.split(/\n/).reverse.grep(/^Received:/).each do |hop| + hop.gsub!(/\bon (.*?) (id.*)/,'; \1') + whence = hop.match(/;\s+(.*)$/)[1] + unless whence + warn "Bad received line: #{hop}" + next + end + from = $+ if hop =~ /from\s+(\S+)|\((.*?)\)/ + by = $1 if hop =~ /by\s+(\S+\.\S+)/ + next unless now = Time.parse(whence).localtime + delta = now - @date + puts out([from, by, hop_date(now), hop_time(delta)]) + @date = now + end + end + + def hop_time(secs) + sign = secs < 0 ? -1 : 1 + days, secs = secs.abs.divmod(60 * 60 * 24) + hours,secs = secs.abs.divmod(60 * 60) + mins, secs = secs.abs.divmod(60) + rtn = "%3ds" % [secs * sign] + rtn << "%3dm" % [mins * sign] if mins != 0 + rtn << "%3dh" % [hours * sign] if hours != 0 + rtn << "%3dd" % [days * sign] if days != 0 + rtn + end +end + +$/ = "" +mail = MailHopDelta.new(ARGF.gets).puts_hops + + +# @@PLEAC@@_4.0 +single_level = [ "this", "that", "the", "other" ] + +# Ruby directly supports nested arrays +double_level = [ "this", "that", [ "the", "other" ] ] +still_single_level = [ "this", "that", [ "the", "other" ] ].flatten + + +# @@PLEAC@@_4.1 +a = [ "quick", "brown", "fox" ] +a = %w(Why are you teasing me?) + +lines = <<"END_OF_HERE_DOC".gsub(/^\s*(.+)/, '\1') + The boy stood on the burning deck, + It was as hot as glass. +END_OF_HERE_DOC + +bigarray = IO.readlines("mydatafile").collect { |l| l.chomp } + +name = "Gandalf" +banner = %Q(Speak, #{name}, and welcome!) + +host_info = `host #{his_host}` + +%x(ps #{$$}) + +banner = 'Costs only $4.95'.split(' ') + +rax = %w! ( ) < > { } [ ] ! + + +# @@PLEAC@@_4.2 +def commify_series(arr) + return '' if not arr + case arr.size + when 0 then '' + when 1 then arr[0] + when 2 then arr.join(' and ') + else arr[0..-2].join(', ') + ', and ' + arr[-1] + end +end + +array = [ "red", "yellow", "green" ] + +print "I have ", array, " marbles\n" +# -> I have redyellowgreen marbles + +# But unlike Perl: +print "I have #{array} marbles\n" +# -> I have redyellowgreen marbles +# So, needs: +print "I have #{array.join(' ')} marbles\n" +# -> I have red yellow green marbles + +#!/usr/bin/ruby +# communify_series - show proper comma insertion in list output + +def commify_series(arr) + return '' if not arr + sepchar = arr.find { |p| p =~ /,/ } ? '; ' : ', ' + case arr.size + when 0 then '' + when 1 then arr[0] + when 2 then arr.join(' and ') + else arr[0..-2].join(sepchar) + sepchar + 'and ' + arr[-1] + end +end + +lists = [ + [ 'just one thing' ], + %w(Mutt Jeff), + %w(Peter Paul Mary), + [ 'To our parents', 'Mother Theresa', 'God' ], + [ 'pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna' ], + [ 'recycle tired, old phrases', 'ponder big, happy thoughts' ], + [ 'recycle tired, old phrases', + 'ponder big, happy thoughts', + 'sleep and dream peacefully' ], +] + +for list in lists do + puts "The list is: #{commify_series(list)}." +end + + +# @@PLEAC@@_4.3 +# (note: AFAIK Ruby doesn't allow gory change of Array length) +# grow the array by assigning nil to past the end of array +ary[new_size-1] = nil +# shrink the array by slicing it down +ary.slice!(new_size..-1) +# init the array with given size +Array.new(number_of_elems) +# assign to an element past the original end enlarges the array +ary[index_new_last_elem] = value + +def what_about_that_array(a) + print "The array now has ", a.size, " elements.\n" + # Index of last element is not really interesting in Ruby + print "Element #3 is `#{a[3]}'.\n" +end +people = %w(Crosby Stills Nash Young) +what_about_that_array(people) + + +# @@PLEAC@@_4.4 +# OO style +bad_users.each { |user| + complain(user) +} +# or, functional style +for user in bad_users + complain(user) +end + +for var in ENV.keys.sort + puts "#{var}=#{ENV[var]}" +end + +for user in all_users + disk_space = get_usage(user) + if (disk_space > MAX_QUOTA) + complain(user) + end +end + +for l in IO.popen("who").readlines + print l if l =~ /^gc/ +end + +# we can mimic the obfuscated Perl way +while fh.gets # $_ is set to the line just read + chomp # $_ has a trailing \n removed, if it had one + split.each { |w| # $_ is split on whitespace + # but $_ is not set to each chunk as in Perl + print w.reverse + } +end +# ...or use a cleaner way +for l in fh.readlines + l.chomp.split.each { |w| print w.reverse } +end + +# same drawback as in problem 1.4, we can't mutate a Numeric... +array.collect! { |v| v - 1 } + +a = [ .5, 3 ]; b = [ 0, 1 ] +for ary in [ a, b ] + ary.collect! { |v| v * 7 } +end +puts "#{a.join(' ')} #{b.join(' ')}" + +# we can mutate Strings, cool; we need a trick for the scalar +for ary in [ [ scalar ], array, hash.values ] + ary.each { |v| v.strip! } # String#strip rules :) +end + + +# @@PLEAC@@_4.5 +# not relevant in Ruby since we have always references +for item in array + # do somethingh with item +end + + +# @@PLEAC@@_4.6 +unique = list.uniq + +# generate a list of users logged in, removing duplicates +users = `who`.collect { |l| l =~ /(\w+)/; $1 }.sort.uniq +puts("users logged in: #{commify_series(users)}") # see 4.2 for commify_series + + +# @@PLEAC@@_4.7 +a - b +# [ 1, 1, 2, 2, 3, 3, 3, 4, 5 ] - [ 1, 2, 4 ] -> [3, 5] + + +# @@PLEAC@@_4.8 +union = a | b +intersection = a & b +difference = a - b + + +# @@PLEAC@@_4.9 +array1.concat(array2) +# if you will assign to another object, better use: +new_ary = array1 + array2 + +members = [ "Time", "Flies" ] +initiates = [ "An", "Arrow" ] +members += initiates + +members = [ "Time", "Flies" ] +initiates = [ "An", "Arrow" ] +members[2,0] = [ "Like", initiates ].flatten + +members[0] = "Fruit" +members[3,2] = "A", "Banana" + + +# @@PLEAC@@_4.10 +reversed = ary.reverse + +ary.reverse_each { |e| + # do something with e +} + +descending = ary.sort.reverse +descending = ary.sort { |a,b| b <=> a } + + +# @@PLEAC@@_4.11 +# remove n elements from front of ary (shift n) +front = ary.slice!(0, n) + +# remove n elements from the end of ary (pop n) +end_ = ary.slice!(-n .. -1) + +# let's extend the Array class, to make that useful +class Array + def shift2() + slice!(0 .. 1) # more symetric with pop2... + end + def pop2() + slice!(-2 .. -1) + end +end + +friends = %w(Peter Paul Mary Jim Tim) +this, that = friends.shift2 + +beverages = %w(Dew Jolt Cola Sprite Fresca) +pair = beverages.pop2 + + +# @@PLEAC@@_4.12 +# use Enumerable#detect (or the synonym Enumerable#find) +highest_eng = employees.detect { |emp| emp.category == 'engineer' } + + +# @@PLEAC@@_4.13 +# use Enumerable#select (or the synonym Enumerable#find_all) +bigs = nums.select { |i| i > 1_000_000 } +pigs = users.keys.select { |k| users[k] > 1e7 } + +matching = `who`.select { |u| u =~ /^gnat / } + +engineers = employees.select { |e| e.position == 'Engineer' } + +secondary_assistance = applicants.select { |a| + a.income >= 26_000 && a.income < 30_000 +} + + +# @@PLEAC@@_4.14 +# normally you would have an array of Numeric (Float or +# Fixnum or Bignum), so you would use: +sorted = unsorted.sort +# if you have strings representing Integers or Floats +# you may specify another sort method: +sorted = unsorted.sort { |a,b| a.to_f <=> b.to_f } + +# let's use the list of my own PID's +`ps ux`.split("\n")[1..-1]. + select { |i| i =~ /^#{ENV['USER']}/ }. + collect { |i| i.split[1] }. + sort { |a,b| a.to_i <=> b.to_i }.each { |i| puts i } +puts "Select a process ID to kill:" +pid = gets.chomp +raise "Exiting ... \n" unless pid && pid =~ /^\d+$/ +Process.kill('TERM', pid.to_i) +sleep 2 +Process.kill('KILL', pid.to_i) + +descending = unsorted.sort { |a,b| b.to_f <=> a.to_f } + + +# @@PLEAC@@_4.15 +ordered = unordered.sort { |a,b| compare(a,b) } + +precomputed = unordered.collect { |e| [compute, e] } +ordered_precomputed = precomputed.sort { |a,b| a[0] <=> b[0] } +ordered = ordered_precomputed.collect { |e| e[1] } + +ordered = unordered.collect { |e| [compute, e] }. + sort { |a,b| a[0] <=> b[0] }. + collect { |e| e[1] } + +for employee in employees.sort { |a,b| a.name <=> b.name } + print employee.name, " earns \$ ", employee.salary, "\n" +end + +# Beware! `0' is true in Ruby. +# For chaining comparisons, you may use Numeric#nonzero?, which +# returns num if num is not zero, nil otherwise +sorted = employees.sort { |a,b| (a.name <=> b.name).nonzero? || b.age <=> a.age } + +users = [] +# getpwent is not wrapped in Ruby... let's fallback +IO.readlines('/etc/passwd').each { |u| users << u.split(':') } +users.sort! { |a,b| a[0] <=> b[0] } +for user in users + puts user[0] +end + +sorted = names.sort { |a,b| a[1, 1] <=> b[1, 1] } +sorted = strings.sort { |a,b| a.length <=> b.length } + +# let's show only the compact version +ordered = strings.collect { |e| [e.length, e] }. + sort { |a,b| a[0] <=> b[0] }. + collect { |e| e[1] } + +ordered = strings.collect { |e| [/\d+/.match(e)[0].to_i, e] }. + sort { |a,b| a[0] <=> b[0] }. + collect { |e| e[1] } + +print `cat /etc/passwd`.collect { |e| [e, e.split(':').indexes(3,2,0)].flatten }. + sort { |a,b| (a[1] <=> b[1]).nonzero? || (a[2] <=> b[2]).nonzero? || a[3] <=> b[3] }. + collect { |e| e[0] } + + +# @@PLEAC@@_4.16 +circular.unshift(circular.pop) # the last shall be first +circular.push(circular.shift) # and vice versa + +def grab_and_rotate(l) + l.push(ret = l.shift) + ret +end + +processes = [1, 2, 3, 4, 5] +while (1) + process = grab_and_rotate(processes) + puts "Handling process #{process}" + sleep 1 +end + + +# @@PLEAC@@_4.17 +def fisher_yates_shuffle(a) + (a.size-1).downto(1) { |i| + j = rand(i+1) + a[i], a[j] = a[j], a[i] if i != j + } +end + +def naive_shuffle(a) + for i in 0...a.size + j = rand(a.size) + a[i], a[j] = a[j], a[i] + end +end + + diff --git a/tests/examplefiles/pppoe.applescript b/tests/examplefiles/pppoe.applescript new file mode 100644 index 0000000..4cb380e --- /dev/null +++ b/tests/examplefiles/pppoe.applescript @@ -0,0 +1,10 @@ +tell application "System Events" + tell network preferences + tell current location + set aPPPoEService to a reference to (first service whose kind is 10) + if exists aPPPoEService then + connect aPPPoEService + end if + end tell + end tell +end tell diff --git a/tests/examplefiles/py3_test.txt b/tests/examplefiles/py3_test.txt new file mode 100644 index 0000000..21fea75 --- /dev/null +++ b/tests/examplefiles/py3_test.txt @@ -0,0 +1,2 @@ +class Käse: + pass diff --git a/tests/examplefiles/pycon_test.pycon b/tests/examplefiles/pycon_test.pycon new file mode 100644 index 0000000..5ed452e --- /dev/null +++ b/tests/examplefiles/pycon_test.pycon @@ -0,0 +1,8 @@ +>>> : + File "", line 1 + : + ^ +SyntaxError: invalid syntax +>>> +KeyboardInterrupt +>>> diff --git a/tests/examplefiles/pytb_test2.pytb b/tests/examplefiles/pytb_test2.pytb new file mode 100644 index 0000000..c4d2033 --- /dev/null +++ b/tests/examplefiles/pytb_test2.pytb @@ -0,0 +1,2 @@ + File "temp.py", line 1 +SyntaxError: Non-ASCII character '\xc3' in file temp.py on line 1, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details diff --git a/tests/examplefiles/python25-bsd.mak b/tests/examplefiles/python25-bsd.mak new file mode 100644 index 0000000..51c2596 --- /dev/null +++ b/tests/examplefiles/python25-bsd.mak @@ -0,0 +1,234 @@ +# New ports collection makefile for: python25 +# Date created: 3 July 2003 +# Whom: Hye-Shik Chang +# +# $FreeBSD: ports/lang/python25/Makefile,v 1.145 2007/10/03 23:22:04 edwin Exp $ + +PORTNAME= python25 +PORTVERSION= 2.5.1 +CATEGORIES= lang python ipv6 +MASTER_SITES= ${PYTHON_MASTER_SITES} +MASTER_SITE_SUBDIR= ${PYTHON_MASTER_SITE_SUBDIR} +DISTFILES= ${PYTHON_DISTFILE} + +MAINTAINER= python@FreeBSD.org +COMMENT?= An interpreted object-oriented programming language + +DIST_SUBDIR= python +WRKSRC= ${PYTHON_WRKSRC}/portbld.static +PATCH_WRKSRC= ${PYTHON_WRKSRC} +GNU_CONFIGURE= yes +CONFIGURE_TARGET= --build=${MACHINE_ARCH}-portbld-freebsd${OSREL} +CONFIGURE_SCRIPT= ../configure # must be relative +CONFIGURE_ENV= OPT="${CFLAGS}" SVNVERSION="echo freebsd" +MAKE_ENV= VPATH="${PYTHON_WRKSRC}" +INSTALLS_SHLIB= yes +INSTALL_TARGET= altinstall +MAN1= ${PYTHON_VERSION}.1 + +USE_PYTHON= yes +PYTHON_VERSION= python2.5 +PYTHON_NO_DEPENDS= yes + +SHARED_WRKSRC= ${PYTHON_WRKSRC}/portbld.shared +PLIST= ${WRKDIR}/PLIST +PLIST_TEMPLATE?=${PKGDIR}/pkg-plist +PLIST_SUB= PYVER=${PYTHON_VERSION:S/python//} \ + PYVER_WITHPAT=${PORTVERSION:S/.c/c/} +DEMODIR= ${PREFIX}/share/examples/${PYTHON_VERSION} +TOOLSDIR= ${PREFIX}/share/${PYTHON_VERSION} + +BIN_SCRIPTS= idle pydoc python python-shared smtpd.py python-config \ + python-shared-config +BINLINKS_SUB= -e 's,smtpd,smtpd${PYTHON_VER},' \ + -e 's,(idle|pydoc|python-shared|python),\1${PYTHON_VER},' + +OPTIONS= THREADS "Enable thread support" on \ + HUGE_STACK_SIZE "Use a larger thread stack" off \ + UCS4 "Use UCS4 for unicode support" on \ + PYMALLOC "Use python's internal malloc" on \ + IPV6 "Enable IPv6 support" on \ + FPECTL "Enable floating point exception handling" off + +.include + +.if ${PYTHON_VERSION} == ${PYTHON_DEFAULT_VERSION} +MLINKS= ${PYTHON_VERSION}.1 python.1 +PLIST_SUB+= IF_DEFAULT="" +.else +PLIST_SUB+= IF_DEFAULT="@comment " +.endif + +# workaround for a bug in base curses.h. +CFLAGS+= -D__wchar_t=wchar_t + +.if !defined(WITHOUT_THREADS) +CONFIGURE_ARGS+= --with-threads +CFLAGS+= ${PTHREAD_CFLAGS} +.if defined(WITHOUT_HUGE_STACK_SIZE) +CFLAGS+= -DTHREAD_STACK_SIZE=0x20000 +.else +CFLAGS+= -DTHREAD_STACK_SIZE=0x100000 +.endif # defined(WITHOUT_HUGE_STACK_SIZE) +CONFIGURE_ENV+= LDFLAGS="${PTHREAD_LIBS} ${LDFLAGS}" +.else +CONFIGURE_ARGS+= --without-threads +.if defined(LDFLAGS) +CONFIGURE_ENV+= LDFLAGS="${LDFLAGS}" +.endif # defined(LDFLAGS) +.endif # !defined(WITHOUT_THREADS) + +.if !defined(WITHOUT_UCS4) && !defined(WITH_UCS2) +CONFIGURE_ARGS+= --enable-unicode=ucs4 +.endif + +.if defined(WITHOUT_PYMALLOC) +CONFIGURE_ARGS+= --without-pymalloc +.endif + +.if ${ARCH} == i386 +PLIST_SUB+= X86_ONLY="" +.else +PLIST_SUB+= X86_ONLY="@comment " +.endif +.if ${ARCH} == amd64 || ${ARCH} == ia64 || ${ARCH} == sparc64 || ${ARCH} == alpha +PLIST_SUB+= 32BIT_ONLY="@comment " +.else +PLIST_SUB+= 32BIT_ONLY="" +.endif +.if ${ARCH} == sparc64 +CFLAGS+= -DPYTHON_DEFAULT_RECURSION_LIMIT=900 +.endif + +.if !exists(/usr/bin/ypcat) # the world with NO_NIS +PLIST_SUB+= NO_NIS="@comment " +.else +PLIST_SUB+= NO_NIS="" +.endif + +.if !defined(WITHOUT_IPV6) +CONFIGURE_ARGS+= --enable-ipv6 +.else +CONFIGURE_ARGS+= --disable-ipv6 +.endif + +.if defined(WITH_FPECTL) +CONFIGURE_ARGS+= --with-fpectl +.endif + +.if ${OSVERSION} >= 700000 +PLATFORMS=plat-freebsd4 plat-freebsd5 plat-freebsd6 +.elif ${OSVERSION} >= 600000 +PLATFORMS=plat-freebsd4 plat-freebsd5 plat-freebsd7 +.else +PLATFORMS=plat-freebsd4 plat-freebsd6 plat-freebsd7 +.endif + +pre-patch: + ${MKDIR} ${WRKSRC} ${SHARED_WRKSRC}/Modules + ${SED} -e '1s,^.*$$,#!${PREFIX}/bin/${PYTHON_VERSION},' \ + ${PATCH_WRKSRC}/Tools/scripts/pydoc > ${WRKDIR}/pydoc2.5 + ${SED} -e '1s,^.*$$,#!${PREFIX}/bin/${PYTHON_VERSION},' \ + ${PATCH_WRKSRC}/Tools/scripts/idle > ${WRKDIR}/idle2.5 + ${SED} -e '1s,^.*$$,#!${PREFIX}/bin/${PYTHON_VERSION},' \ + ${PATCH_WRKSRC}/Lib/smtpd.py > ${WRKDIR}/smtpd2.5.py + ${REINPLACE_CMD} -e \ + 's,/usr/doc/python-docs-,${PREFIX}/share/doc/python,g' \ + ${PATCH_WRKSRC}/Lib/pydoc.py + ${REINPLACE_CMD} -e \ + 's|^\( *prefixes = .*\)\]$$|\1, "${X11BASE}"]|g' \ + ${PATCH_WRKSRC}/Lib/site.py + ${REINPLACE_CMD} -e \ + 's|^ \(..ASDLGEN.*\)$$| ${TRUE}|g' \ + ${PATCH_WRKSRC}/Makefile.pre.in + + ${REINPLACE_CMD} -e \ + 's|*\(..INSTALL_SCRIPT.*\)python-config$$|#port \1|' \ + ${PATCH_WRKSRC}/Makefile.pre.in + + ${SED} -e 's|^#!.*|#!${PREFIX}/bin/${PYTHON_VERSION}|' \ + ${PATCH_WRKSRC}/Misc/python-config.in > ${WRKDIR}/${PYTHON_VERSION}-config + ${SED} -e 's|^#!.*|#!${PREFIX}/bin/${PYTHON_VERSION:S/thon/thon-shared/}|' \ + ${PATCH_WRKSRC}/Misc/python-config.in > ${WRKDIR}/${PYTHON_VERSION:S/thon/thon-shared/}-config + +.if defined(WITH_FPECTL) && ${ARCH} == i386 + ${MKDIR} ${WRKSRC}/Modules + ${ECHO} "fpectl fpectlmodule.c" >> ${WRKSRC}/Modules/Setup.dist +.endif + +post-configure: + ${TAR} -C ${WRKSRC} -cf - . | ${TAR} -C ${SHARED_WRKSRC} -xf - + ${LN} -sf ${PYTHON_WRKSRC}/Lib ${WRKSRC}/Lib + ${SED} -e 's,^\(LDLIBRARY=\).*$$,\1libpython$$(VERSION).so,' \ + -e 's,^\(BLDLIBRARY=\).*$$,\1-L. -lpython$$(VERSION),' \ + -e 's,^\(CFLAGSFORSHARED=\).*$$,\1$$(CCSHARED),' \ + -e 's,^\(Makefile Modules/config.c:.*\)Makefile.pre,\1,' \ + -e 's,^\(.(BUILDPYTHON)\: .*\).(LIBRARY),\1,' \ + -e 's,^\(.(BUILDPYTHON):.*\).(LIBRARY),\1,' \ + ${WRKSRC}/Makefile > ${SHARED_WRKSRC}/Makefile + +pre-build: + cd ${SHARED_WRKSRC}; \ + ${SETENV} ${MAKE_ENV} ${MAKE} lib${PYTHON_VERSION}.so python; \ + ${LN} -f lib${PYTHON_VERSION}.so lib${PYTHON_VERSION}.so.1; \ + ${LN} -f python ${PYTHON_VERSION:S/thon/thon-shared/} + +pre-su-install: +.for platform in ${PLATFORMS} + ${MKDIR} ${PYTHONPREFIX_LIBDIR}/${platform} +.for file in IN.py regen + ${INSTALL_DATA} ${WRKSRC}/Lib/${platform}/${file} \ + ${PYTHONPREFIX_LIBDIR}/${platform}/ +.endfor +.endfor + +pre-install: + ${CAT} ${PLIST_TEMPLATE} | ${AWK} '{ print $$0; } \ + /LIBDIR.*\.py$$/ && !/\/bad/ { print $$0 "o"; print $$0 "c"; }' > ${PLIST} + + @# if openssl 0.9.8 is detected, _sha{256,512} module won't be installed + ([ -f ${WRKSRC}/.without_own_sha ] && \ + ${GREP} -v 'lib-dynload/_sha' ${PLIST} > ${PLIST}.tmp && \ + ${CAT} ${PLIST}.tmp > ${PLIST}) || ${TRUE} + +post-install: + @# install config providers + ${INSTALL_SCRIPT} ${WRKDIR}/${PYTHON_VERSION}-config ${PREFIX}/bin + ${INSTALL_SCRIPT} ${WRKDIR}/${PYTHON_VERSION:S/thon/thon-shared/}-config ${PREFIX}/bin + + @# shared version of executable and library + ${INSTALL_PROGRAM} ${SHARED_WRKSRC}/lib${PYTHON_VERSION}.so.1 \ + ${PREFIX}/lib + cd ${PREFIX}/lib; ${LN} -sf lib${PYTHON_VERSION}.so.1 \ + lib${PYTHON_VERSION}.so + ${LN} -sf ${PREFIX}/lib/lib${PYTHON_VERSION}.so ${PYTHONPREFIX_LIBDIR}/config + ${INSTALL_PROGRAM} \ + ${SHARED_WRKSRC}/${PYTHON_VERSION:S/thon/thon-shared/} \ + ${PREFIX}/bin + + @# additional files installing by ports + ${INSTALL_SCRIPT} ${WRKDIR}/pydoc2.5 ${WRKDIR}/idle2.5 \ + ${WRKDIR}/smtpd2.5.py ${PREFIX}/bin + @${MKDIR} ${MANPREFIX}/man/man1 + ${INSTALL_MAN} ${PYTHON_WRKSRC}/Misc/python.man \ + ${MANPREFIX}/man/man1/${PYTHON_VERSION}.1 + +.if ${PYTHON_VERSION} == ${PYTHON_DEFAULT_VERSION} + for f in ${BIN_SCRIPTS}; do \ + TARGET=`${ECHO_CMD} $$f | ${SED} -E ${BINLINKS_SUB}`; \ + cd ${PREFIX}/bin && ${LN} -f $$TARGET $$f; \ + done +.endif + +.if !defined(NOPORTDOCS) + @${MKDIR} ${TOOLSDIR} + @cd ${PYTHON_WRKSRC}; ${TAR} -cf - Tools | \ + (cd ${TOOLSDIR}; ${TAR} -xf -) + @${MKDIR} ${DEMODIR} + @cd ${PYTHON_WRKSRC}/Demo; ${TAR} -cf - * | \ + (cd ${DEMODIR}; ${TAR} -xf -) +.endif + + @${CAT} ${PKGMESSAGE} + +.include diff --git a/tests/examplefiles/qsort.prolog b/tests/examplefiles/qsort.prolog new file mode 100644 index 0000000..d78de6f --- /dev/null +++ b/tests/examplefiles/qsort.prolog @@ -0,0 +1,13 @@ +partition([], _, [], []). +partition([X|Xs], Pivot, Smalls, Bigs) :- + ( X @< Pivot -> + Smalls = [X|Rest], + partition(Xs, Pivot, Rest, Bigs) + ; Bigs = [X|Rest], + partition(Xs, Pivot, Smalls, Rest) + ). + +quicksort([]) --> []. +quicksort([X|Xs]) --> + { partition(Xs, X, Smaller, Bigger) }, + quicksort(Smaller), [X], quicksort(Bigger). diff --git a/tests/examplefiles/ragel-cpp_rlscan b/tests/examplefiles/ragel-cpp_rlscan new file mode 100644 index 0000000..4b14632 --- /dev/null +++ b/tests/examplefiles/ragel-cpp_rlscan @@ -0,0 +1,280 @@ +/* + * Lexes Ragel input files. + * + * @LANG: c++ + * + * Test works with split code gen. + */ + +#include +#include +#include +#include + +using namespace std; + +void escapeXML( const char *data ) +{ + while ( *data != 0 ) { + switch ( *data ) { + case '<': cout << "<"; break; + case '>': cout << ">"; break; + case '&': cout << "&"; break; + default: cout << *data; break; + } + data += 1; + } +} + +void escapeXML( char c ) +{ + switch ( c ) { + case '<': cout << "<"; break; + case '>': cout << ">"; break; + case '&': cout << "&"; break; + default: cout << c; break; + } +} + +void escapeXML( const char *data, int len ) +{ + for ( const char *end = data + len; data != end; data++ ) { + switch ( *data ) { + case '<': cout << "<"; break; + case '>': cout << ">"; break; + case '&': cout << "&"; break; + default: cout << *data; break; + } + } +} + +inline void write( const char *data ) +{ + cout << data; +} + +inline void write( char c ) +{ + cout << c; +} + +inline void write( const char *data, int len ) +{ + cout.write( data, len ); +} + + +%%{ + machine RagelScan; + + word = [a-zA-Z_][a-zA-Z_0-9]*; + integer = [0-9]+; + hex = '0x' [0-9a-fA-F] [0-9a-fA-F]*; + + default = ^0; + EOF = 0; + + # Handles comments in outside code and inline blocks. + c_comment := + ( default* :>> '*/' ) + ${ escapeXML( fc ); } + @{ fret; }; + + action emit { + escapeXML( ts, te-ts ); + } + + # + # Inline action code + # + + ilscan := |* + + "'" ( [^'\\] | /\\./ )* "'" => emit; + '"' ( [^"\\] | /\\./ )* '"' => emit; + '/*' { + write( "/*" ); + fcall c_comment; + }; + '//' [^\n]* '\n' => emit; + + '{' { + write( '{' ); + inline_depth += 1; + }; + + '}' { + write( '}' ); + /* If dropping down to the last } then return + * to ragel code. */ + if ( --inline_depth == 0 ) { + write( "\n" ); + fgoto rlscan; + } + }; + + default => { escapeXML( *ts ); }; + *|; + + # + # Ragel Tokens + # + + rlscan := |* + '}%%' { + if ( !single_line ) { + write( "\n" ); + fgoto main; + } + }; + + '\n' { + if ( single_line ) { + write( "\n" ); + fgoto main; + } + }; + + # Word + word { + write( "" ); + write( ts, te-ts ); + write( "\n" ); + }; + + # Decimal integer. + integer { + write( "" ); + write( ts, te-ts ); + write( "\n" ); + }; + + # Hexidecimal integer. + hex { + write( "" ); + write( ts, te-ts ); + write( "\n" ); + }; + + # Consume comments. + '#' [^\n]* '\n'; + + # Single literal string. + "'" ( [^'\\] | /\\./ )* "'" { + write( "" ); + escapeXML( ts, te-ts ); + write( "\n" ); + }; + + # Double literal string. + '"' ( [^"\\] | /\\./ )* '"' { + write( "" ); + escapeXML( ts, te-ts ); + write( "\n" ); + }; + + # Or literal. + '[' ( [^\]\\] | /\\./ )* ']' { + write( "" ); + escapeXML( ts, te-ts ); + write( "\n" ); + }; + + # Regex Literal. + '/' ( [^/\\] | /\\./ ) * '/' { + write( "" ); + escapeXML( ts, te-ts ); + write( "\n" ); + }; + + # Open an inline block + '{' { + inline_depth = 1; + write( "{" ); + fgoto ilscan; + }; + + punct { + write( "" ); + escapeXML( fc ); + write( "\n" ); + }; + + default; + *|; + + # + # Outside code. + # + + main := |* + + "'" ( [^'\\] | /\\./ )* "'" => emit; + '"' ( [^"\\] | /\\./ )* '"' => emit; + + '/*' { + escapeXML( ts, te-ts ); + fcall c_comment; + }; + + '//' [^\n]* '\n' => emit; + + '%%{' { + write( "
    \n" ); + single_line = false; + fgoto rlscan; + }; + + '%%' { + write( "
    \n" ); + single_line = true; + fgoto rlscan; + }; + + default { + escapeXML( *ts ); + }; + + # EOF. + EOF; + *|; +}%% + +%% write data nofinal; + +void test( const char *data ) +{ + std::ios::sync_with_stdio(false); + + int cs, act; + const char *ts, *te; + int stack[1], top; + + bool single_line = false; + int inline_depth = 0; + + %% write init; + + /* Read in a block. */ + const char *p = data; + const char *pe = data + strlen( data ); + const char *eof = pe; + %% write exec; + + if ( cs == RagelScan_error ) { + /* Machine failed before finding a token. */ + cerr << "PARSE ERROR" << endl; + exit(1); + } +} + +#define BUFSIZE 2048 + +int main() +{ + std::ios::sync_with_stdio(false); + + test("hi %%{ /'}%%'/ { /*{*/ {} } + '\\'' }%%there\n"); + + return 0; +} diff --git a/tests/examplefiles/ragel-cpp_snippet b/tests/examplefiles/ragel-cpp_snippet new file mode 100644 index 0000000..203ae28 --- /dev/null +++ b/tests/examplefiles/ragel-cpp_snippet @@ -0,0 +1,2 @@ + %% write init; + /* Read in a block. */ diff --git a/tests/examplefiles/regex.js b/tests/examplefiles/regex.js new file mode 100644 index 0000000..7790cb0 --- /dev/null +++ b/tests/examplefiles/regex.js @@ -0,0 +1,22 @@ +// regex + +blah(/abc/); +x = /abc/; +x = /abc/.match; + +// math + +blah(1/2); //comment +x = 1 / 2 / 3; +x = 1/1/.1; + +// broken + +x=/1/; +x=1/a/g; +x=a/a/g; + +// real-world + +var x = 1/(1+Math.sqrt(sum)); // convert to number between 1-0 +return Math.round((num / den) * 100)/100; diff --git a/tests/examplefiles/ruby_func_def.rb b/tests/examplefiles/ruby_func_def.rb new file mode 100644 index 0000000..a820c68 --- /dev/null +++ b/tests/examplefiles/ruby_func_def.rb @@ -0,0 +1,11 @@ +class (get_foo("blub"))::Foo + def (foo("bar") + bar("baz")).something argh, aaahaa + 42 + end +end + +class get_the_fuck("out")::Of::My + def parser_definition + ruby! + end +end diff --git a/tests/examplefiles/sibling.prolog b/tests/examplefiles/sibling.prolog new file mode 100644 index 0000000..bc59150 --- /dev/null +++ b/tests/examplefiles/sibling.prolog @@ -0,0 +1,19 @@ +/* Comments /* can nest */ +still a comment +*/ + +:- module(maplist, maplist/3) + +assert(world:done). % asserts + +sibling(X, Y) :- parent_child(Z, X), parent_child(Z, Y). + +parent_child(X, Y) :- father_child(X, Y). +parent_child(X, Y) :- mother_child(X, Y). + +mother_child(trude, sally). + +father_child(tom, sally). +father_child(tom, erica). +father_child(mike, tom). + diff --git a/tests/examplefiles/simple.md b/tests/examplefiles/simple.md new file mode 100644 index 0000000..8f12771 --- /dev/null +++ b/tests/examplefiles/simple.md @@ -0,0 +1,747 @@ +module simple; + +// Importing stuff. +{ + function loadMod(name, ns) + { + assert(name == "mod"); + + ns.x = "I'm x"; + + ns.foo = function foo() + { + writefln("foo"); + }; + + ns.bar = function bar(x) + { + return x[0]; + }; + + ns.baz = function baz() + { + writefln(x); + }; + + foreach(k, v; ns) + if(isFunction(v)) + v.environment(ns); + } + + setModuleLoader("mod", loadMod); + + import mod : foo, bar; + foo(); + writefln(bar([5])); + mod.baz(); + + writefln(); +} + +// Super calls. +{ + class Base + { + function fork() + { + writefln("Base fork."); + } + } + + class Derived : Base + { + function fork() + { + writefln("Derived fork!"); + super.fork(); + } + } + + local d = Derived(); + d.fork(); + + writefln(); +} + +// Coroutines and coroutine iteration. +{ + local countDown = coroutine function countDown(x) + { + yield(); + + while(x > 0) + { + yield(x); + x--; + } + }; + + foreach(v; countDown, 5) + writefln(v); + + writefln(); + + local forEach = coroutine function forEach(t) + { + yield(); + + foreach(k, v; t) + yield(k, v); + }; + + foreach(_, k, v; forEach, {hi = 1, bye = 2}) + writefln("key: ", k, ", value: ", v); + + writefln(); +} + +// Testing tailcalls. +{ + function recurse(x) + { + writefln("recurse: ", x); + + if(x == 0) + return toString(x); + else + return recurse(x - 1); + } + + writefln(recurse(5)); + writefln(); + + class A + { + function f(x) + { + writefln("A.f: ", x); + + if(x == 0) + return toString(x); + else + return this.f(x - 1); // call it as this.f to force a 'method' instruction to be generated + } + } + + local a = A(); + writefln(a.f(5)); + writefln(); +} + +{ + // A function which lets us define properties for a class. + // The varargs should be a bunch of tables, each with a 'name' field, and 'getter' and/or 'setter' fields. + function mixinProperties(classType, vararg) + { + classType.mProps = { }; + + classType.opIndex = function opIndex(key) + { + local prop = mProps[key]; + + if(prop is null) + throw format(classType, ".opIndex() - Property '%s' does not exist", key); + + local getter = prop.getter; + + if(getter is null) + throw format(classType, ".opIndex() - Property '%s' has no getter", key); + + return getter(with this); + }; + + classType.opIndexAssign = function opIndexAssign(key, value) + { + local prop = mProps[key]; + + if(prop is null) + throw format(classType, ".opIndexAssign() - Property '%s' does not exist", key); + + local setter = prop.setter; + + if(setter is null) + throw format(classType, ".opIndexAssign() - Property '%s' has no setter", key); + + setter(with this, value); + }; + + foreach(i, prop; [vararg]) + { + if(!isTable(prop)) + throw format("mixinProperties() - property ", i, " is not a table"); + + if(prop.name is null) + throw format("mixinProperties() - property ", i, " has no name"); + + if(prop.setter is null && prop.getter is null) + throw format("mixinProperties() - property '%s' has no getter or setter", prop.name); + + classType.mProps[prop.name] = prop; + } + } + + // Create a class to test out. + class PropTest + { + mX = 0; + mY = 0; + mName = ""; + + function constructor(name) + { + mName = name; + } + + function toString() + { + return format("name = '", mName, "' x = ", mX, " y = ", mY); + } + } + + // Mix in the properties. + mixinProperties + ( + PropTest, + + { + name = "x", + + function setter(value) + { + mX = value; + } + + function getter() + { + return mX; + } + }, + + { + name = "y", + + function setter(value) + { + mY = value; + } + + function getter() + { + return mY; + } + }, + + { + name = "name", + + function getter() + { + return mName; + } + } + ); + + // Create an instance and try it out. + local p = PropTest("hello"); + + writefln(p); + p.x = 46; + p.y = 123; + p.x = p.x + p.y; + writefln(p); + + // Try to access a nonexistent property. + try + p.name = "crap"; + catch(e) + { + writefln("caught: ", e); + writefln(getTraceback()); + } + + writefln(); +} + +// Some container classes. +{ + class PQ + { + mData; + mLength = 0; + + function constructor() + { + mData = array.new(15); + } + + function insert(data) + { + resizeArray(); + mData[mLength] = data; + + local index = mLength; + local parentIndex = (index - 1) / 2; + + while(index > 0 && mData[parentIndex] > mData[index]) + { + local temp = mData[parentIndex]; + mData[parentIndex] = mData[index]; + mData[index] = temp; + + index = parentIndex; + parentIndex = (index - 1) / 2; + } + + mLength += 1; + } + + function remove() + { + if(mLength == 0) + throw "PQ.remove() - No items to remove"; + + local data = mData[0]; + mLength -= 1; + mData[0] = mData[mLength]; + + local index = 0; + local left = 1; + local right = 2; + + while(index < mLength) + { + local smaller; + + if(left >= mLength) + { + if(right >= mLength) + break; + else + smaller = right; + } + else + { + if(right >= mLength) + smaller = left; + else + { + if(mData[left] < mData[right]) + smaller = left; + else + smaller = right; + } + } + + if(mData[index] > mData[smaller]) + { + local temp = mData[index]; + mData[index] = mData[smaller]; + mData[smaller] = temp; + + index = smaller; + left = (index * 2) + 1; + right = left + 1; + } + else + break; + } + + return data; + } + + function resizeArray() + { + if(mLength >= #mData) + mData.length((#mData + 1) * 2 - 1); + } + + function hasData() + { + return mLength != 0; + } + } + + class Stack + { + mHead = null; + + function push(data) + { + local t = { data = data, next = mHead }; + mHead = t; + } + + function pop() + { + if(mHead is null) + throw "Stack.pop() - No items to pop"; + + local item = mHead; + mHead = mHead.next; + + return item.data; + } + + function hasData() + { + return mHead !is null; + } + } + + class Queue + { + mHead = null; + mTail = null; + + function push(data) + { + local t = { data = data, next = null }; + + if(mTail is null) + { + mHead = t; + mTail = t; + } + else + { + mTail.next = t; + mTail = t; + } + } + + function pop() + { + if(mTail is null) + throw "Queue.pop() - No items to pop"; + + local item = mHead; + mHead = mHead.next; + + if(mHead is null) + mTail = null; + + return item.data; + } + + function hasData() + { + return mHead !is null; + } + } + + writefln("Priority queue (heap)"); + + local prioQ = PQ(); + + for(i : 0 .. 10) + prioQ.insert(math.rand(0, 20)); + + while(prioQ.hasData()) + writefln(prioQ.remove()); + + writefln(); + writefln("Stack"); + + local stack = Stack(); + + for(i : 0 .. 5) + stack.push(i + 1); + + while(stack.hasData()) + writefln(stack.pop()); + + writefln(); + writefln("Queue"); + + local queue = Queue(); + + for(i : 0 .. 5) + queue.push(i + 1); + + while(queue.hasData()) + writefln(queue.pop()); + + writefln(); +} + +// opApply tests. +{ + class Test + { + mData = [4, 5, 6]; + + function opApply(extra) + { + if(isString(extra) && extra == "reverse") + { + local function iterator_reverse(index) + { + index--; + + if(index < 0) + return; + + return index, mData[index]; + } + + return iterator_reverse, this, #mData; + } + else + { + local function iterator(index) + { + index++; + + if(index >= #mData) + return; + + return index, mData[index]; + } + + return iterator, this, -1; + } + } + } + + local test = Test(); + + foreach(k, v; test) + writefln("test[", k, "] = ", v); + + writefln(); + + foreach(k, v; test, "reverse") + writefln("test[", k, "] = ", v); + + writefln(); + + test = + { + fork = 5, + knife = 10, + spoon = "hi" + }; + + foreach(k, v; test) + writefln("test[", k, "] = ", v); + + test = [5, 10, "hi"]; + + writefln(); + + foreach(k, v; test) + writefln("test[", k, "] = ", v); + + writefln(); + + foreach(k, v; test, "reverse") + writefln("test[", k, "] = ", v); + + writefln(); + + foreach(k, v; "hello") + writefln("str[", k, "] = ", v); + + writefln(); + + foreach(k, v; "hello", "reverse") + writefln("str[", k, "] = ", v); + + writefln(); +} + +// Testing upvalues in for loops. +{ + local arr = array.new(10); + + for(i : 0 .. 10) + arr[i] = function() { return i; }; + + writefln("This should be the values 0 through 9:"); + + foreach(func; arr) + writefln(func()); + + writefln(); +} + +// Testing nested functions. +{ + function outer() + { + local x = 3; + + function inner() + { + x++; + writefln("inner x: ", x); + } + + writefln("outer x: ", x); + inner(); + writefln("outer x: ", x); + + return inner; + } + + local func = outer(); + func(); + + writefln(); +} + +// Testing Exceptions. +{ + function thrower(x) + { + if(x >= 3) + throw "Sorry, x is too big for me!"; + } + + function tryCatch(iterations) + { + try + { + for(i : 0 .. iterations) + { + writefln("tryCatch: ", i); + thrower(i); + } + } + catch(e) + { + writefln("tryCatch caught: ", e); + throw e; + } + finally + writefln("tryCatch finally"); + } + + try + { + tryCatch(2); + tryCatch(5); + } + catch(e) + writefln("caught: ", e); + + writefln(); +} + +// Testing arrays. +{ + local array = [7, 9, 2, 3, 6]; + + array.sort(); + + foreach(i, v; array) + writefln("arr[", i, "] = ", v); + + array ~= ["foo", "far"]; + + writefln(); + + foreach(i, v; array) + writefln("arr[", i, "] = ", v); + + writefln(); +} + +// Testing vararg functions. +{ + function vargs(vararg) + { + local args = [vararg]; + + writefln("num varargs: ", #args); + + foreach(i, v; args) + writefln("args[", i, "] = ", v); + } + + vargs(); + + writefln(); + + vargs(2, 3, 5, "foo", "bar"); + + writefln(); +} + +// Testing switches. +{ + foreach(v; ["hi", "bye", "foo"]) + { + switch(v) + { + case "hi": + writefln("switched to hi"); + break; + + case "bye": + writefln("switched to bye"); + break; + + default: + writefln("switched to something else"); + break; + } + } + + writefln(); + + foreach(v; [null, false, 1, 2.3, 'x', "hi"]) + { + switch(v) + { + case null: writefln("null"); break; + case false: writefln("false"); break; + case 1: writefln("1"); break; + case 2.3: writefln("2.3"); break; + case 'x': writefln("x"); break; + case "hi": writefln("hi"); break; + } + } + + writefln(); + + class A + { + mValue; + + this(value) + { + mValue = value; + } + + function opCmp(other) + { + assert(other as A); + return mValue <=> other.mValue; + } + } + + local a1 = A(1); + local a2 = A(2); + local a3 = A(3); + + for(s : 1 .. 4) + { + local ss = A(s); + + switch(ss) + { + case a1: + writefln(1); + break; + + case a2: + writefln(2); + break; + + case a3: + writefln(3); + break; + } + } +} \ No newline at end of file diff --git a/tests/examplefiles/smarty_example.html b/tests/examplefiles/smarty_example.html new file mode 100644 index 0000000..cf4ffdc --- /dev/null +++ b/tests/examplefiles/smarty_example.html @@ -0,0 +1,209 @@ +{php} + include "some/php/file.php"; + + foreach ($rows as $row) { + echo $row; + } +{/php} + +{* smarty comment *} + + {serendipity_hookPlugin hook="entries_header" addData="$entry_id"} + + {foreach from=$entries item="dategroup"} + + {foreachelse} + {if not $plugin_clean_page} + {$CONST.NO_ENTRIES_TO_PRINT} + {/if} + {/foreach} + +{if $footer_info} + {/if} + {serendipity_hookPlugin hook="entries_footer"} + diff --git a/tests/examplefiles/source.lgt b/tests/examplefiles/source.lgt new file mode 100644 index 0000000..ce5abce --- /dev/null +++ b/tests/examplefiles/source.lgt @@ -0,0 +1,343 @@ + +% this is a single-line comment + +/* +this is +a block +comment +*/ + + +:- encoding(some_encoding). +:- op(Precedence, Associativity, Operator). + + +:- object(prototype, + implements(protocol), + imports(category), + extends(parent)). + + :- info([ + version is 1.0, + author is 'Paulo Moura', + date is 2008/5/1, + comment is 'Sample prototype for testing syntax coloring.']). + :- threaded. + :- synchronized. + :- dynamic. + :- initialization(some_goal(X, Y)). + :- calls(some_other_protocol). + :- uses(another_object). + + :- alias(set, member/2, set_member/2). + :- alias(words, singular//0, peculiar//0). + + :- uses(list, [append/3, member/2]). + :- uses(queues, [new/1::new_queue/1]). + + :- public(aaa/2). + :- meta_predicate(aaa(::, *)). + :- discontiguous(aaa/2). + :- mode(aaa(+callable, ?integer), zero_or_one). + :- info(position/2, [ + comment is 'Predicate brief description.', + arguments is ['Arg1'-'Arg1 description', 'Arg2'-'Arg2 description']]). + + :- protected(bbb/2). + :- synchronized(bbb/2). + :- mode(bbb(+integer, -float), one). + :- info(bbb/2, [ + comment is 'Predicate brief description.', + argnames is ['Arg1', 'Arg2']]). + + :- private(ccc/2). + :- dynamic(ccc/2). + :- mode(ccc(@atom, ?atom), one_or_more). + :- info(ccc/2, [ + comment is 'Predicate brief description.', + argnames is ['Arg1', 'Arg2']]). + + enumerating_entities(Object, Protocol, Category) :- + current_category(Category), + current_object(Object), + current_protocol(Protocol). + + enumerating_properties :- + category_property(Category, Property), + object_property(Object, Property), + protocol_property(Protocol, Property). + + creating_entities(Object, Protocol, Category) :- + create_category(Category, Relations, Directives, Clauses), + create_object(Object, Relations, Directives, Clauses), + create_protocol(Protocol, Relations, Directives). + + abolishing_entities(Object, Protocol, Category) :- + abolish_category(Category), + abolish_object(Object), + abolish_protocol(Protocol). + + entity_relations :- + extends_object(Prototype, Parent, Scope), + extends_protocol(Protocol1, Protocol2, Scope), + extends_category(Category1, Category2, Scope), + implements_protocol(Object, Protocol, Scope), + imports_category(Object, Category, Scope), + instantiates_class(Instance, Class, Scope), + specializes_class(Class, Superclass, Scope), + complements_object(Category, Object). + + event_handling :- + abolish_events(Event, Object, Message, Sender, Monitor), + current_event(Event, Object, Message, Sender, Monitor), + define_events(Event, Object, Message, Sender, Monitor). + + multi_threading :- + threaded(Goals), + threaded_call(Goal), + threaded_once(Goal), + threaded_ignore(Goal), + threaded_exit(Goal), + threaded_peek(Goal), + threaded_wait(Goal), + threaded_notify(Notification). + + compiling_and_loading :- + logtalk_compile(File, Options), + logtalk_load(File, Options), + logtalk_library_path(Library, Path). + + flags :- + current_logtalk_flag(Flag, Value), + set_logtalk_flag(Flag, Value). + + execution_context_methods :- + parameter(N, Parameter), + self(Self), + sender(Sender), + this(This). + + reflection_methods :- + current_predicate(Predicate), + predicate_property(Predicate, Property). + + database_methods :- + abolish(Functor/Arity), + asserta(Clause), + assertz(Clause), + clause(Head, Body), + retract(Clause), + retractall(Head). + + meta_call_methods :- + call(Goal). + + all_solutions_methods :- + bagof(Term, Goal, List), + findall(Term, Goal, List), + forall(Generate, Test), + setof(Term, Goal, List). + + event_handler_methods :- + before(Object, Message, Sender), + after(Object, Message, Sender). + + dcg_rules_parsing_methods :- + phrase(NonTerminal, Input, Rest). + + term_expansion_methods :- + expand_term(Term, Expanded), + term_expansion(Term, Expanded), + goal_expansion(Goal, Expanded). + + message_sending :- + Object::Message, + ::Message, + ^^Message. + + calling_external_code :- + {goal1, goal2, goal3}. + + context_switching_calls :- + Object< + Then + ; Else + ). + + numbers :- + X is 13, + Y is 13.13, + Z is 13.13e-23, + C1 is 0'A, C2 is 0'', C3 is 0'", + B is 0b1011101, + O is 0o1234560, + H is 0x1234567890abcDEF. + + functions :- + A is atan(3.14) + sin(0.77) - cos(123.23), + B is sign(-12) * abs(35/78), + C is truncate(3.14) + round(-7.8) - ceiling(111.88), + D is exp(3.8) - log(123.98) / sqrt(33) * 23 ** 4, + E is rem(3, 2) + mod(5, 3) * 2 rem 2 // 5 mod 3, + F is float_fractional_part(3.14) + float_integer_part(3.14), + G is float(33) + floor(99.99). + + bitwise :- + A is 16 >> 2, + B is 16 << 2, + C is 10 /\ 12, + D is 10 \/ 12, + E is \ 10. + + term_unification :- + Term1 = Term2, + Term1 \= Term2, + unify_with_occurs_check(Term1, Term2). + + term_testing :- + atom(Atom), + atomic(Atomic), + integer(Integer), + float(Float), + compound(Term), + nonvar(Term), + var(Term), + number(Number). + + term_comparison :- + Term1 == Term2, + Term1 \== Term2, + Term1 @< Term2, + Term1 @=< Term2, + Term1 @>= Term2, + Term1 @> Term2. + + term_creation_and_decomposition :- + functor(Term, Functor, Arity), + arg(N, Term, Arg), + Term =.. [Functor| Args], + copy_term(Term, Copy). + + arithemtic_evaluation :- + X is Expression. + + arithemtic_comparison :- + Exp1 =:= Exp2, + Exp1 =\= Exp2, + Exp1 < Exp2, + Exp1 =< Exp2, + Exp1 > Exp2, + Exp1 >= Exp2. + + stream_selection_and_control :- + current_input(Stream), + current_output(Stream), + set_input(Stream), + set_output(Stream), + open(Source, Mode, Stream, Options), + close(Stream), + flush_output(Stream), + stream_property(Stream, Property), + at_end_of_stream(Stream), + set_stream_position(Stream, Position), + flush_output, + at_end_of_stream. + + character_input_output :- + get_char(Char), + get_code(Code), + peek_char(Char), + peek_code(Code), + put_char(Char), + put_code(Code), + nl(Stream), + nl. + + byte_input_output :- + get_byte(Byte), + peek_byte(Byte), + put_byte(Byte). + + term_input_output :- + read(Term), + read_term(Term), + write(Term), + write(Term), + write_canonical(Term), + write_term(Stream, Term, Options), + current_op(Precedence, Associativity, Operator), + op(Precedence, Associativity, Operator), + current_char_conversion(InChar, OutChar), + char_conversion(InChar, OutChar). + + logic_and_control :- + \+ Goal, + once(Goal), + repeat, + !. + + atomic_term_processing :- + atom_length(Atom, Length), + atom_chars(Atom, Chars), + atom_codes(Atom, Codes), + atom_concat(Atom1, Atom2, Atom), + sub_atom(Atom, Before, Length, After, SubAtom), + char_code(Char, Code), + number_chars(Number, Chars), + number_codes(Number, Codes). + + implementation_defined_hooks :- + current_prolog_flag(Flag, Value), + set_prolog_flag(Flag, Value), + halt(ExitCode), + halt. + + number(C) --> "+", number(C). + number(C) --> "-", number(X), {C is -X}. + number(X) --> [C], {0'0 =< C, C =< 0'9, X is C - 0'0}. + +:- end_object. + + + +:- object(class, + implements(protocol), + imports(category), + instantiates(metaclass), + specializes(superclass)). + + +:- end_object. + + + +:- object(parametric(_Par1, _Par2), + implements(protocol), + imports(category), + extends(parent(_Par))). + + +:- end_object. + + + +:- category(category, + implements(protocol), + extends(other_category)). + + +:- end_category. + + + +:- protocol(extended, + extends(minimal)). + + +:- end_protocol. diff --git a/tests/examplefiles/sources.list b/tests/examplefiles/sources.list new file mode 100644 index 0000000..3f36335 --- /dev/null +++ b/tests/examplefiles/sources.list @@ -0,0 +1,62 @@ +## CD ROM +deb cdrom:[Xubuntu 6.06.1 _Dapper Drake_ - Release i386 (20060807)]/ dapper main restricted + +deb http://archive.ubuntu.com/ubuntu/ dapper main restricted +deb-src http://archive.ubuntu.com/ubuntu/ dapper main restricted + +deb http://foo.com/$(ARCH)/ main foo + +## Major bug fix updates produced after the final release of the +## distribution. +deb http://archive.ubuntu.com/ubuntu/ dapper-updates main restricted +deb-src http://archive.ubuntu.com/ubuntu/ dapper-updates main restricted + +## Uncomment the following two lines to add software from the 'universe' +## repository. +## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu +## team, and may not be under a free licence. Please satisfy yourself as to +## your rights to use the software. Also, please note that software in +## universe WILL NOT receive any review or updates from the Ubuntu security +## team. +deb http://archive.ubuntu.com/ubuntu/ dapper universe multiverse +deb-src http://archive.ubuntu.com/ubuntu/ dapper universe multiverse + +## Uncomment the following two lines to add software from the 'backports' +## repository. +## N.B. software from this repository may not have been tested as +## extensively as that contained in the main release, although it includes +## newer versions of some applications which may provide useful features. +## Also, please note that software in backports WILL NOT receive any review +## or updates from the Ubuntu security team. +deb http://archive.ubuntu.com/ubuntu/ dapper-backports main restricted universe multiverse +deb-src http://archive.ubuntu.com/ubuntu/ dapper-backports main restricted universe multiverse + +deb http://security.ubuntu.com/ubuntu dapper-security main restricted +deb-src http://security.ubuntu.com/ubuntu dapper-security main restricted +deb http://security.ubuntu.com/ubuntu dapper-security universe multiverse +deb-src http://security.ubuntu.com/ubuntu dapper-security universe multiverse + +## dapper-commercial by canonical +## currently has realplay (realplayer 10) and opera (opera 9) +deb http://archive.canonical.com/ubuntu dapper-commercial main + +## Bleeding edge wine repository for Dapper +## only uncomment it if you need it +## deb http://wine.budgetdedicated.com/apt dapper main +## deb-src http://wine.budgetdedicated.com/apt dapper main + +## skype +## only uncomment it if you need it +## deb http://download.skype.com/linux/repos/debian/ stable non-free + +deb http://de.archive.ubuntu.com/ubuntu/ edgy main restricted multiverse universe + +deb http://de.archive.ubuntu.com/ubuntu/ edgy-updates main restricted multiverse universe + +deb http://de.archive.ubuntu.com/ubuntu/ edgy-backports main restricted universe multiverse + +deb http://security.ubuntu.com/ubuntu edgy-security main restricted universe multiverse + +deb http://wine.budgetdedicated.com/apt edgy main + +deb http://archive.czessi.net/ubuntu edgy main restricted universe multiverse i18n-de diff --git a/tests/examplefiles/sphere.pov b/tests/examplefiles/sphere.pov new file mode 100644 index 0000000..847ed45 --- /dev/null +++ b/tests/examplefiles/sphere.pov @@ -0,0 +1,18 @@ +#include "colors.inc" + +background { color Cyan } + +camera { + location <0, 2, -3> + look_at <0, 1, 2> +} + +sphere { + <0, 1, 2>, 2 + texture { + pigment { color Yellow } + } +} + +light_source { <2, 4, -3> color White} + diff --git a/tests/examplefiles/sqlite3.sqlite3-console b/tests/examplefiles/sqlite3.sqlite3-console new file mode 100644 index 0000000..3ec2713 --- /dev/null +++ b/tests/examplefiles/sqlite3.sqlite3-console @@ -0,0 +1,27 @@ +SQLite version 3.4.2 +Enter ".help" for instructions +sqlite> .schema +CREATE TABLE paste (paste_id integer, code text, parsed_code text, pub_date +varchar(24), language varchar(64), parent_id integer, url varchar(128)); +CREATE TABLE vars (key varchar(24), value varchar(128)); +sqlite> a ' + ...> ' + ...> ; +SQL error: near "a": syntax error +sqlite> %; +SQL error: near "%": syntax error +sqlite> select count(language), language from paste group by language order + ...> by count(language) desc; +144|python +76|text +22|pycon +9|ruby +7|c +7|js +6|html+django +4|html +4|tex +2|html+php +1|cpp +1|scheme +sqlite> diff --git a/tests/examplefiles/squid.conf b/tests/examplefiles/squid.conf new file mode 100644 index 0000000..31e611d --- /dev/null +++ b/tests/examplefiles/squid.conf @@ -0,0 +1,27 @@ +# First, a comment block for the deafult conf: + +# TAG: buffered_logs on|off +# cache.log log file is written with stdio functions, and as such +# it can be buffered or unbuffered. By default it will be unbuffered. +# Buffering it can speed up the writing slightly (though you are +# unlikely to need to worry unless you run with tons of debugging +# enabled in which case performance will suffer badly anyway..). +# +#Default: +# buffered_logs off + +# Now, a slightly useful (but in no way complete) set of options: + +cache_peer upstream1.example.com parent 8080 0 no-query proxy-only round-robin +cache_peer upstream2.example.com parent 3128 0 no-query proxy-only round-robin +never_direct allow all +never_direct allow CONNECT + +acl myclients src 127.0.0.1 +http_access allow myclients + +acl mynet src 192.168.0.0/255.255.0.0 +no_cache deny mynet + +acl mynetlocal dst 192.168.0.0/255.255.0.0 +always_direct allow mynetlocal diff --git a/tests/examplefiles/string_delimiters.d b/tests/examplefiles/string_delimiters.d new file mode 100644 index 0000000..288aacc --- /dev/null +++ b/tests/examplefiles/string_delimiters.d @@ -0,0 +1,21 @@ +import std.stdio; + +void main() { + // Nesting delimited strings + auto a = q"{foo " {bar} baz}"; + auto b = q"[foo [bar] " baz]"; + auto c = q"(foo " (bar) baz)"; + auto d = q" " baz>"; + // Non-nesting delimited strings + auto e = q"/foo " bar/"; + auto f = q"-Another " string-"; + // "heredoc" strings + auto g = q"FOO + This is a string! +FOO"; + // Token strings (only the q{} should be highlighted as a string) + auto h = q{ + int i; + void foo() { writefln("Hello, world!"); } + }; +} diff --git a/tests/examplefiles/test.R b/tests/examplefiles/test.R new file mode 100644 index 0000000..c53edd1 --- /dev/null +++ b/tests/examplefiles/test.R @@ -0,0 +1,119 @@ +################################### +####### emplikH1.test() ########## +################################### + +emplikH1.test <- function(x, d, theta, fun, + tola = .Machine$double.eps^.25) +{ +n <- length(x) +if( n <= 2 ) stop("Need more observations") +if( length(d) != n ) stop("length of x and d must agree") +if(any((d!=0)&(d!=1))) stop("d must be 0/1's for censor/not-censor") +if(!is.numeric(x)) stop("x must be numeric values --- observed times") + +#temp<-summary(survfit(Surv(x,d),se.fit=F,type="fleming",conf.type="none")) +# +newdata <- Wdataclean2(x,d) +temp <- DnR(newdata$value, newdata$dd, newdata$weight) + +time <- temp$time # only uncensored time? Yes. +risk <- temp$n.risk +jump <- (temp$n.event)/risk + +funtime <- fun(time) +funh <- (n/risk) * funtime # that is Zi +funtimeTjump <- funtime * jump + +if(jump[length(jump)] >= 1) funh[length(jump)] <- 0 #for inthaz and weights + +inthaz <- function(x, ftj, fh, thet){ sum(ftj/(1 + x * fh)) - thet } + +diff <- inthaz(0, funtimeTjump, funh, theta) + +if( diff == 0 ) { lam <- 0 } else { + step <- 0.2/sqrt(n) + if(abs(diff) > 6*log(n)*step ) + stop("given theta value is too far away from theta0") + + mini<-0 + maxi<-0 + if(diff > 0) { + maxi <- step + while(inthaz(maxi, funtimeTjump, funh, theta) > 0 && maxi < 50*log(n)*step) + maxi <- maxi+step + } + else { + mini <- -step + while(inthaz(mini, funtimeTjump, funh, theta) < 0 && mini > - 50*log(n)*step) + mini <- mini - step + } + + if(inthaz(mini, funtimeTjump, funh, theta)*inthaz(maxi, funtimeTjump, funh, theta) > 0 ) + stop("given theta is too far away from theta0") + + temp2 <- uniroot(inthaz,c(mini,maxi), tol = tola, + ftj=funtimeTjump, fh=funh, thet=theta) + lam <- temp2$root +} + +onepluslamh<- 1 + lam * funh ### this is 1 + lam Zi in Ref. + +weights <- jump/onepluslamh #need to change last jump to 1? NO. see above + +loglik <- 2*(sum(log(onepluslamh)) - sum((onepluslamh-1)/onepluslamh) ) +#?is that right? YES see (3.2) in Ref. above. This ALR, or Poisson LR. + +#last <- length(jump) ## to compute loglik2, we need to drop last jump +#if (jump[last] == 1) { +# risk1 <- risk[-last] +# jump1 <- jump[-last] +# weights1 <- weights[-last] +# } else { +# risk1 <- risk +# jump1 <- jump +# weights1 <- weights +# } +#loglik2 <- 2*( sum(log(onepluslamh)) + +# sum( (risk1 -1)*log((1-jump1)/(1- weights1) ) ) ) +##? this likelihood seems have negative values sometimes??? + +list( logemlik=loglik, ### logemlikv2=loglik2, + lambda=lam, times=time, wts=weights, + nits=temp2$nf, message=temp2$message ) +} + +library("graphics") + +par(mfrow = c(1, 2)) +# plot histogram +x <- rnorm(100) +if (max(x) > 100) + stop("Quite unexpected.") +else + hist(x, plot=TRUE, col="ivory") + +# from doc: lowess +plot(cars, main = "lowess(cars)") + lines(lowess(cars), col = 2) + lines(lowess(cars, f=.2), col = 3) + legend(5, 120, c(paste("f = ", c("2/3", ".2"))), lty = 1, col = 2:3) + +# from doc: is.na +is.na(c(1, NA)) + +# from doc: Extract +y <- list(1,2,a=4,5) +y[c(3,4)] # a list containing elements 3 and 4 of y +y$a # the element of y named a + +# from doc: for +for(n in c(2,5,10,20,50)) { + x <- stats::rnorm(n) + cat(n,":", sum(x2),"\n") +} + +class(fo <- y ~ x1*x2) # "formula" + + + + diff --git a/tests/examplefiles/test.bas b/tests/examplefiles/test.bas new file mode 100644 index 0000000..af5f257 --- /dev/null +++ b/tests/examplefiles/test.bas @@ -0,0 +1,29 @@ +Public Class Form1 + Inherits System.Windows.Forms.Form + + Private t As New System.Timers.Timer(2000) + + Private Sub Form1_Load(ByVal sender As Object, _ + ByVal e As System.EventArgs) Handles MyBase.Load + + AddHandler t.Elapsed, AddressOf TimerFired + End Sub + + Private Sub btnStart_Click(ByVal sender As System.Object, _ + ByVal e As System.EventArgs) Handles btnStart.Click + + t.Enabled = True + End Sub + + Private Sub btnStop_Click(ByVal sender As System.Object, _ + ByVal e As System.EventArgs) Handles btnStop.Click + + t.Enabled = False + End Sub + + Public Sub TimerFired(ByVal sender As Object, _ + ByVal e As System.Timers.ElapsedEventArgs) + + Label1.Text = "Signal Time = " & e.SignalTime.ToString + End Sub +End Class diff --git a/tests/examplefiles/test.boo b/tests/examplefiles/test.boo new file mode 100644 index 0000000..d6107aa --- /dev/null +++ b/tests/examplefiles/test.boo @@ -0,0 +1,39 @@ +import System +import Boo.Lang.Interpreter from Boo.Lang.Interpreter + +class ObjectInterpreter(AbstractInterpreter): + + _context as object + + [getter(Value)] + _value as object + + def constructor(context): + _context = context + self.RememberLastValue = true + + override def Lookup(name as string): + property = _context.GetType().GetProperty(name) + return property.PropertyType if property is not null + + override def GetValue(name as string): + return _context.GetType().GetProperty(name).GetValue( + _context, null) + + override def SetLastValue(value): + _value = value + + override def SetValue(name as string, value): + raise InvalidOperationException() + + override def Declare(name as string, type as Type): + raise InvalidOperationException() + +class Person: + [property(FirstName)] + _fname as string = "" + +p = Person(FirstName: "Homer") +i = ObjectInterpreter(p) +i.Eval('"Hello, ${FirstName.ToUpper()}!"') +print i.Value diff --git a/tests/examplefiles/test.cs b/tests/examplefiles/test.cs new file mode 100644 index 0000000..ffa9bfe --- /dev/null +++ b/tests/examplefiles/test.cs @@ -0,0 +1,351 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// MIT X11 license, Copyright (c) 2005-2006 by: // +// // +// Authors: // +// Michael Dominic K. // +// // +// Permission is hereby granted, free of charge, to any person obtaining a // +// copy of this software and associated documentation files (the "Software"), // +// to deal in the Software without restriction, including without limitation // +// the rights to use, copy, modify, merge, publish, distribute, sublicense, // +// and/or sell copies of the Software, and to permit persons to whom the // +// Software is furnished to do so, subject to the following conditions: // +// // +// The above copyright notice and this permission notice shall be included // +// in all copies or substantial portions of the Software. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // +// USE OR OTHER DEALINGS IN THE SOFTWARE. // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace Diva.Core { + + using System; + using Widgets; + using System.Xml; + using Util; + using System.Collections.Generic; + using System.Collections; + using Basics; + + public class OpenerTask : Task, IBoilProvider { + + // Private structs //////////////////////////////////////////// + + struct ObjectInfo { + + public ObjectContainer Container; + public int[] Depends; + public string SystemType; + public int RefId; + + /* CONSTRUCTOR */ + public ObjectInfo (ObjectContainer container) + { + Container = container; + Depends = container.Depends.ToArray (); + SystemType = container.SystemType; + RefId = container.RefId; + } + + public override string ToString () + { + return String.Format ("Type: {0} Deps count: {1} Id: {2}", + SystemType, Depends.Length, RefId); + } + + public bool IsUnBoilable (IBoilProvider provider) + { + if (Depends.Length == 0) + return true; + + foreach (int id in Depends) + if (! (provider.Contains (id))) + return false; + + return true; + } + + } + + // Enums ////////////////////////////////////////////////////// + + enum OpenerTaskStep { Init, Header, ProjectInfoRead, ObjectListRead, + ObjectListParse, ObjectListUnBoil, FindRoots, + Finished }; + + // Fields ///////////////////////////////////////////////////// + + string fileName; // Filename we're reading + XmlDocument xmlDocument; // Our document + //XmlNode projectInfoNode; // node + IEnumerator objectsEnumerator; // Enumerator + List objectsList; // Objects list + ObjectListContainer objectListContainer; + OpenerTaskStep currentStep; // Our current step + + Dictionary idToObject; // Id -> object + Dictionary objectToId; // Object -> Id + + string projectName = String.Empty; + string projectDirectory = String.Empty; + TagList projectTagList; + StuffList projectStuffList; + TrackList projectTrackList; + ClipList projectClipList; + MediaItemList projectMediaItemList; + Commander projectCommander; + Gdv.Pipeline projectPipeline; + Gdv.ProjectFormat projectFormat; + + // Properties ///////////////////////////////////////////////// + + public string ProjectName { + get { return projectName; } + } + + public string ProjectDirectory { + get { return projectDirectory; } + } + + public TagList ProjectTagList { + get { return projectTagList; } + } + + public StuffList ProjectStuffList { + get { return projectStuffList; } + } + + public TrackList ProjectTrackList { + get { return projectTrackList; } + } + + public ClipList ProjectClipList { + get { return projectClipList; } + } + + public MediaItemList ProjectMediaItemList { + get { return projectMediaItemList; } + } + + public Commander ProjectCommander { + get { return projectCommander; } + } + + public Gdv.Pipeline ProjectPipeline { + get { return projectPipeline; } + } + + public Gdv.ProjectFormat ProjectFormat { + get { return projectFormat; } + } + + // Public methods ///////////////////////////////////////////// + + /* CONSTRUCTOR */ + public OpenerTask (string fileName) + { + this.fileName = fileName; + } + + public override void Reset () + { + objectToId = new Dictionary (); + idToObject = new Dictionary (); + + xmlDocument = null; + //projectInfoNode = null; + + currentStep = OpenerTaskStep.Init; + + base.Reset (); + } + + public int GetIdForObject (object o) + { + return objectToId [o]; + } + + public object GetObjectForId (int id) + { + return idToObject [id]; + } + + public bool Contains (int id) + { + return idToObject.ContainsKey (id); + } + + // Private methods //////////////////////////////////////////// + + protected override TaskStatus ExecuteStep (int s) + { + bool cont = true; + + // Main + switch (currentStep) { + + case OpenerTaskStep.Init: + objectsList = new List (); + xmlDocument = new XmlDocument (); + xmlDocument.Load (fileName); + currentStep = OpenerTaskStep.Header; + break; + + case OpenerTaskStep.Header: + //ReadHeader (); + currentStep = OpenerTaskStep.ProjectInfoRead; + break; + + case OpenerTaskStep.ProjectInfoRead: + foreach (XmlNode node in xmlDocument.DocumentElement.ChildNodes) + if (node.Name == "projectinfo") + ResolveProjectInfoNode (node); + + // FIXME: Fail if not found/not resolved + currentStep = OpenerTaskStep.ObjectListRead; + break; + + case OpenerTaskStep.ObjectListRead: + foreach (XmlNode node in xmlDocument.DocumentElement.ChildNodes) + if (node.Name == "objectlist") + objectListContainer = (ObjectListContainer) + DataFactory.MakeDataElement (node as XmlElement); + + if (objectListContainer == null) + throw new Exception ("ObjectListContainer not found!"); + + currentStep = OpenerTaskStep.ObjectListParse; + break; + + case OpenerTaskStep.ObjectListParse: + bool flush = EnumerateSomeObjects (); + if (flush) + currentStep = OpenerTaskStep.ObjectListUnBoil; + break; + + case OpenerTaskStep.ObjectListUnBoil: + bool done = UnBoilSomeObjects (); + if (done) + currentStep = OpenerTaskStep.FindRoots; + break; + + + case OpenerTaskStep.FindRoots: + projectTrackList = (TrackList) FindRoot ("tracklist"); + projectTagList = (TagList) FindRoot ("taglist"); + projectStuffList = (StuffList) FindRoot ("stufflist"); + projectClipList = (ClipList) FindRoot ("cliplist"); + projectMediaItemList = (MediaItemList) FindRoot ("mediaitemlist"); + projectPipeline = (Gdv.Pipeline) FindRoot ("pipeline"); + projectCommander = (Commander) FindRoot ("commander"); + projectFormat = (Gdv.ProjectFormat) FindRoot ("projectformat"); + + currentStep = OpenerTaskStep.Finished; + break; + + case OpenerTaskStep.Finished: + cont = false; + break; + + default: + break; + } + + // Post + if (cont) + return TaskStatus.Running; + else + return TaskStatus.Done; + } + + /* + void ReadHeader () + { + // FIXME: Read all the attributes from the element + }*/ + + void ResolveProjectInfoNode (XmlNode node) + { + foreach (XmlNode childNode in node) { + + switch (childNode.Name) { + + case "name": + projectName = childNode.FirstChild.Value; + break; + + case "directory": + projectDirectory = childNode.FirstChild.Value; + break; + + // FIXME: Duration etc. + } + } + } + + bool EnumerateSomeObjects () + { + if (objectsEnumerator == null) + objectsEnumerator = objectListContainer.FindAllObjects ().GetEnumerator (); + + for (int i = 0; i < 10; i++) { + if (objectsEnumerator.MoveNext () == false) + return true; + + ObjectContainer container = (ObjectContainer) + objectsEnumerator.Current; + + ObjectInfo newInfo = new ObjectInfo (container); + objectsList.Add (newInfo); + } + + return false; + } + + ObjectInfo GetNextCandidate () + { + foreach (ObjectInfo objInfo in objectsList) + if (objInfo.IsUnBoilable (this)) + return objInfo; + + throw new Exception ("FIXME: No more unboilable objects found. Recursive?"); + } + + bool UnBoilSomeObjects () + { + for (int i = 0; i < 5; i++) { + // All unboiled + if (objectsList.Count == 0) + return true; + + ObjectInfo objInfo = GetNextCandidate (); + + object o = BoilFactory.UnBoil (objInfo.Container, this); + objectsList.Remove (objInfo); + + // Add + idToObject [objInfo.RefId] = o; + objectToId [o] = objInfo.RefId; + + } + + return false; + } + + object FindRoot (string rootString) + { + ObjectContainer container = objectListContainer.FindObjectContainer (rootString); + return idToObject [container.RefId]; + } + + } + +} diff --git a/tests/examplefiles/test.css b/tests/examplefiles/test.css new file mode 100644 index 0000000..e59ad30 --- /dev/null +++ b/tests/examplefiles/test.css @@ -0,0 +1,49 @@ +body { + font-size: 12pt; + background: #fff url(temp.png) top left no-repeat; +} + +* html body { + font-size: 14pt; +} + +#nav .new { + display: block; +} + +ul#nav li.new { + font-weight: bold; +} + +:link { + color: #f00; +} + +:link:hover { + color: #0f0; +} + +@media screen { + body { + background: #ccc; + } +} + +@namespace "http://www.w3.org/1999/xhtml"; + +@import url("mystyle.css"); + +@charset "ISO-8859-1"; + +@font-face { font-family: "Example Font"; src: url("http://www.example.com/fonts/example"); } + +@media screen { body { font-size: 16px } } @media print { body { font-size: 12pt } } + + +@page { body { margin: 1in 1.5in; } } + +@page linke-seite:left { body { margin:20mm; margin-right:25mm; } } + +@-moz-document url-prefix(http://pygments.org) { a {font-style: normal !important;} } + + diff --git a/tests/examplefiles/test.d b/tests/examplefiles/test.d new file mode 100644 index 0000000..02fe8f7 --- /dev/null +++ b/tests/examplefiles/test.d @@ -0,0 +1,135 @@ +// Created by Lionello Lunesu and placed in the public domain. +// This file has been modified from its original version. +// It has been formatted to fit your screen. +module phoneno; // optional +import std.stdio; // writefln +import std.ctype; // isdigit +import std.stream; // BufferedFile + +// Just for readability (imagine char[][][char[]]) +alias char[] string; +alias string[] stringarray; + +/// Strips non-digit characters from the string (COW) +string stripNonDigit( in string line ) +{ + string ret; + foreach(uint i, c; line) { + // Error: std.ctype.isdigit at C:\dmd\src\phobos\std\ctype.d(37) + // conflicts with std.stream.isdigit at C:\dmd\src\phobos\std\stream.d(2924) + if (!std.ctype.isdigit(c)) { + if (!ret) + ret = line[0..i]; + } + else if (ret) + ret ~= c; + } + return ret?ret:line; +} + +unittest { + assert( stripNonDigit("asdf") == "" ); + assert( stripNonDigit("\'13-=2 4kop") == "1324" ); +} + +/// Converts a word into a number, ignoring all non alpha characters +string wordToNum( in string word ) +{ +// translation table for the task at hand +const char[256] TRANSLATE = + " " // 0 + " 0123456789 " // 32 + " 57630499617851881234762239 " // 64 + " 57630499617851881234762239 " + " " + " " + " " + " "; + string ret; + foreach(c; cast(ubyte[])word) + if (TRANSLATE[c] != ' ') + ret ~= TRANSLATE[c]; + return ret; +} + +unittest { + // Test wordToNum using the table from the task description. + assert( "01112223334455666777888999" == + wordToNum("E | J N Q | R W X | D S Y | F T | A M | C I V | B K U | L O P | G H Z")); + assert( "01112223334455666777888999" == + wordToNum("e | j n q | r w x | d s y | f t | a m | c i v | b k u | l o p | g h z")); + assert( "0123456789" == + wordToNum("0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9")); +} + +void main( string[] args ) +{ + // This associative array maps a number to an array of words. + stringarray[string] num2words; + + foreach(string word; new BufferedFile("dictionary.txt" ) ) + num2words[ wordToNum(word) ] ~= word.dup; // must dup + + /// Finds all alternatives for the given number + /// (should have been stripped from non-digit characters) + stringarray _FindWords( string numbers, bool digitok ) + in { + assert(numbers.length > 0); + } + out(result) { + foreach (a; result) + assert( wordToNum(a) == numbers ); + } + body { + stringarray ret; + bool foundword = false; + for (uint t=1; t<=numbers.length; ++t) { + auto alternatives = numbers[0..t] in num2words; + if (!alternatives) + continue; + foundword = true; + if (numbers.length > t) { + // Combine all current alternatives with all alternatives + // of the rest (next piece can start with a digit) + foreach (a2; _FindWords( numbers[t..$], true ) ) + foreach(a1; *alternatives) + ret ~= a1 ~ " " ~ a2; + } + else + ret ~= *alternatives; // append these alternatives + } + // Try to keep 1 digit, only if we're allowed and no other + // alternatives were found + // Testing "ret.length" makes more sense than testing "foundword", + // but the other implementations seem to do just this. + if (digitok && !foundword) { //ret.length == 0 + if(numbers.length > 1) { + // Combine 1 digit with all altenatives from the rest + // (next piece can not start with a digit) + foreach (a; _FindWords( numbers[1..$], false ) ) + ret ~= numbers[0..1] ~ " " ~ a; + } + else + ret ~= numbers[0..1]; // just append this digit + } + return ret; + } + + /// (This function was inlined in the original program) + /// Finds all alternatives for the given phone number + /// Returns: array of strings + stringarray FindWords( string phone_number ) + { + if (!phone_number.length) + return null; + // Strip the non-digit characters from the phone number, and + // pass it to the recursive function (leading digit is allowed) + return _FindWords( stripNonDigit(phone_number), true ); + } + + // Read the phone numbers + foreach(string phone; new BufferedFile("input.txt" ) ) + foreach(alternative; FindWords( phone ) ) + writefln(phone, ": ", alternative ); +} + diff --git a/tests/examplefiles/test.erl b/tests/examplefiles/test.erl new file mode 100644 index 0000000..5b983e7 --- /dev/null +++ b/tests/examplefiles/test.erl @@ -0,0 +1,169 @@ +-module(test). +-export([listen/1, + handle_client/1, + maintain_clients/1, + start/1, + stop/0, + controller/1]). + +-author("jerith"). + +-define(TCP_OPTIONS,[list, {packet, 0}, {active, false}, {reuseaddr, true}]). + +-record(player, {name=none, socket, mode}). + +%% To allow incoming connections, we need to listen on a TCP port. +%% This is also the entry point for our server as a whole, so it +%% starts the client_manager process and gives it a name so the rest +%% of the code can get to it easily. + +listen(Port) -> + {ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS), + register(client_manager, spawn(?MODULE, maintain_clients, [[]])), + do_accept(LSocket). + +%% Accepting a connection gives us a connection socket with the +%% newly-connected client on the other end. Since we want to accept +%% more than one client, we spawn a new process for each and then wait +%% for another connection on our listening socket. + +do_accept(LSocket) -> + case gen_tcp:accept(LSocket) of + {ok, Socket} -> + spawn(?MODULE, handle_client, [Socket]), + client_manager ! {connect, Socket}; + {error, Reason} -> + io:format("Socket accept error: ~s~n", [Reason]) + end, + do_accept(LSocket). + +%% All the client-socket process needs to do is wait for data and +%% forward it to the client_manager process which decides what to do +%% with it. If the client disconnects, we let client_manager know and +%% then quietly go away. + +handle_client(Socket) -> + case gen_tcp:recv(Socket, 0) of + {ok, Data} -> + client_manager ! {data, Socket, Data}, + handle_client(Socket); + {error, closed} -> + client_manager ! {disconnect, Socket} + end. + +%% This is the main loop of the client_manager process. It maintains +%% the list of "players" and calls the handler for client input. + +maintain_clients(Players) -> + io:format("Players:~n", []), + lists:foreach(fun(P) -> io:format(">>> ~w~n", [P]) end, Players), + receive + {connect, Socket} -> + Player = #player{socket=Socket, mode=connect}, + send_prompt(Player), + io:format("client connected: ~w~n", [Player]), + NewPlayers = [Player | Players]; + {disconnect, Socket} -> + Player = find_player(Socket, Players), + io:format("client disconnected: ~w~n", [Player]), + NewPlayers = lists:delete(Player, Players); + {data, Socket, Data} -> + Player = find_player(Socket, Players), + NewPlayers = parse_data(Player, Players, Data), + NewPlayer = find_player(Socket, NewPlayers), + send_prompt(NewPlayer) + end, + maintain_clients(NewPlayers). + +%% find_player is a utility function to get a player record associated +%% with a particular socket out of the player list. + +find_player(Socket, Players) -> + {value, Player} = lists:keysearch(Socket, #player.socket, Players), + Player. + +%% delete_player returns the player list without the given player. It +%% deletes the player from the list based on the socket rather than +%% the whole record because the list might hold a different version. + +delete_player(Player, Players) -> + lists:keydelete(Player#player.socket, #player.socket, Players). + +%% Sends an appropriate prompt to the player. Currently the only +%% prompt we send is the initial "Name: " when the player connects. + +send_prompt(Player) -> + case Player#player.mode of + connect -> + gen_tcp:send(Player#player.socket, "Name: "); + active -> + ok + end. + +%% Sends the given data to all players in active mode. + +send_to_active(Prefix, Players, Data) -> + ActivePlayers = lists:filter(fun(P) -> P#player.mode == active end, + Players), + lists:foreach(fun(P) -> gen_tcp:send(P#player.socket, Prefix ++ Data) end, + ActivePlayers), + ok. + +%% We don't really do much parsing, but that will probably change as +%% more features are added. Currently this handles naming the player +%% when he first connects and treats everything else as a message to +%% send. + +parse_data(Player, Players, Data) -> + case Player#player.mode of + active -> + send_to_active(Player#player.name ++ ": ", + delete_player(Player, Players), Data), + Players; + connect -> + UPlayer = Player#player{name=bogostrip(Data), mode=active}, + [UPlayer | delete_player(Player, Players)] + end. + +%% Utility methods to clean up the name before we apply it. Called +%% bogostrip rather than strip because it returns the first continuous +%% block of non-matching characters rather stripping matching +%% characters off the front and back. + +bogostrip(String) -> + bogostrip(String, "\r\n\t "). + +bogostrip(String, Chars) -> + LStripped = string:substr(String, string:span(String, Chars)+1), + string:substr(LStripped, 1, string:cspan(LStripped, Chars)). + +%% Here we have some extra code to test other bits of pygments' Erlang +%% lexer. + +get_timestamp() -> + {{Year,Month,Day},{Hour,Min,Sec}} = erlang:universaltime(), + lists:flatten(io_lib:format( + "~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0BZ", + [Year, Month, Day, Hour, Min, Sec])). + +a_binary() -> + << 100:16/integer, 16#7f >>. + +a_list_comprehension() -> + [X*2 || X <- [1,2,3]]. + +map(Fun, [H|T]) -> + [Fun(H) | map(Fun, T)]; + +map(Fun, []) -> + []. + +%% pmap, just because it's cool. + +pmap(F, L) -> + Parent = self(), + [receive {Pid, Result} -> + Result + end || Pid <- [spawn(fun() -> + Parent ! {self(), F(X)} + end) || X <- L]]. diff --git a/tests/examplefiles/test.evoque b/tests/examplefiles/test.evoque new file mode 100644 index 0000000..5a98d3b --- /dev/null +++ b/tests/examplefiles/test.evoque @@ -0,0 +1,33 @@ +$overlay{name=site_base} + +$begin{table_row} + $for{ col in row } + ${col}\ + $else + empty row + $rof +$end{table_row} + + + $for{ i, row in enumerate(rows) } + #[ "odd" rows get a special style ]# + $evoque{#table_row} + $evoque{ + #table_row + } + $evoque{'#table_row'} + $evoque{ '#table_row', collection=None, quoting="str"} + $evoque{name="#table_row"} + $evoque{name=var_table_row} + $evoque{%#table_row%} + $evoque{% #table_row %} + + $rof +
    + +$evoque{disclaimer, collection="legals"} +$evoque{ disclaimer , collection="legals", abc=123} +$evoque{% disclaimer, collection="legals"%} + +$test{% site_base="site.html", + rows=[("a", "b", 3.0, {"one":1}, "", "i", "j")] %} diff --git a/tests/examplefiles/test.html b/tests/examplefiles/test.html new file mode 100644 index 0000000..39365c0 --- /dev/null +++ b/tests/examplefiles/test.html @@ -0,0 +1,351 @@ + + + + + + +
    # -*- coding: utf-8 -*-
    +"""
    +    pocoo.pkg.core.acl
    +    ~~~~~~~~~~~~~~~~~~
    +
    +    Pocoo ACL System.
    +
    +    :copyright: 2006-2007 by Armin Ronacher.
    +    :license: GNU GPL, see LICENSE for more details.
    +"""
    +
    +from pocoo.db import meta
    +
    +from pocoo.pkg.core.forum import Site, Forum, Thread
    +from pocoo.pkg.core.user import User, Group
    +
    +from pocoo.pkg.core.db import users, groups, group_members, privileges, \
    +     forums, posts, acl_mapping, acl_subjects, acl_objects
    +
    +
    +class AclManager(object):
    +    """
    +    Manager object to manage ALCs.
    +    """
    +    STRONG_NO = -1
    +
    +    WEAK_NO = 0
    +    WEAK_YES = 1
    +    STRONG_YES = 2
    +
    +    def __init__(self, ctx, subject):
    +        self.ctx = ctx
    +
    +        self.subject = subject
    +        if isinstance(subject, User):
    +            self._type = 'user'
    +
    +        elif isinstance(subject, Group):
    +            self._type = 'group'
    +
    +        else:
    +            raise ValueError('neither user or group specified')
    +
    +    def allow(self, privilege, obj, force=False):
    +        """Allows the subject privilege on obj."""
    +
    +        return self._set(privilege, obj, 1 + bool(force))
    +
    +    def default(self, privilege, obj):
    +        """Sets the state for privilege on obj back to weak yes."""
    +
    +        return self._set(privilege, obj, 0)
    +
    +    def deny(self, privilege, obj, force=False):
    +        """Denies the subject privilege on obj."""
    +
    +        return self._set(privilege, obj, -1 - bool(force))
    +
    +    def can_access(self, privilege, obj):
    +        """Checks if the current subject with the required privilege
    +        somehow. Either directly or when the subject is a user and
    +        one of its groups can access it."""
    +
    +        #XXX: maybe this could be one big query instead of 4
    +        #XXX: this currently does not work correctly, therefore return True
    +        return True
    +
    +        if not isinstance(obj, (Forum, Thread, Site.__class__)):
    +            raise TypeError('obj must be a forum, thread or site')
    +        privilege = privilege.upper()
    +        s = self._get_subject_join().alias('s').c
    +
    +        def do_check(obj, tendency):
    +            db = self.ctx.engine
    +
    +            o = self._get_object_join(obj).alias('o').c
    +
    +            # self check
    +            r = db.execute(meta.select([acl_mapping.c.state],
    +                (acl_mapping.c.priv_id == privileges.c.priv_id) &
    +
    +                (acl_mapping.c.subject_id == s.subject_id) &
    +                (acl_mapping.c.object_id == o.object_id) &
    +
    +                (privileges.c.name == privilege)
    +            ))
    +            row = r.fetchone()
    +            if row is not None:
    +                if row['state'] in (self.STRONG_NO, self.STRONG_YES):
    +                    return row['state'] == self.STRONG_YES
    +
    +                tendency = row['state']
    +
    +            # if the controlled subject is a user check all groups
    +            if isinstance(self.subject, User):
    +                r = db.execute(meta.select([acl_mapping.c.state],
    +                    (acl_mapping.c.object_id == o.object_id) &
    +
    +                    (acl_mapping.c.subject_id == groups.c.subject_id) &
    +
    +                    (groups.c.group_id == group_members.c.group_id) &
    +
    +                    (group_members.c.user_id == self.subject.user_id)
    +                ))
    +                while True:
    +                    row = r.fetchone()
    +                    if row is None:
    +                        break
    +
    +                    state = row[0]
    +                    if state in (self.STRONG_YES, self.STRONG_NO):
    +                        return state == self.STRONG_YES
    +
    +                    if tendency is None:
    +                        tendency = state
    +                    elif tendency == self.WEAK_NO and state == self.WEAK_YES:
    +                        tendency = self.WEAK_YES
    +
    +            # check related objects
    +            if isinstance(obj, Thread):
    +                return do_check(obj.forum, tendency)
    +            elif isinstance(obj, Forum):
    +                return do_check(Site, tendency)
    +            else:
    +                return tendency
    +
    +        return do_check(obj, None) in (self.WEAK_YES, self.STRONG_YES)
    +
    +    def _set(self, privilege, obj, state):
    +        """Helper functions for settings privileges."""
    +
    +        privilege = privilege.upper()
    +        if self.subject.subject_id is None:
    +            self._bootstrap()
    +        if obj.object_id is None:
    +            self._bootstrap_object(obj)
    +        # special state "0" which means delete
    +
    +        if not state:
    +            p = meta.select([privileges.c.priv_id], privileges.c.name == privilege)
    +            self.ctx.engine.execute(acl_mapping.delete(
    +                (acl_mapping.c.priv_id == p.c.priv_id) &
    +
    +                (acl_mapping.c.subject_id == self.subject.subject_id) &
    +
    +                (acl_mapping.c.object_id == obj.object_id)
    +            ))
    +            return
    +        # touch privilege and check existing mapping
    +
    +        priv_id = self._fetch_privilege(privilege)
    +        r = self.ctx.engine.execute(meta.select([acl_mapping.c.state],
    +            (acl_mapping.c.priv_id == priv_id) &
    +
    +            (acl_mapping.c.subject_id == self.subject.subject_id) &
    +
    +            (acl_mapping.c.object_id == obj.object_id)
    +        ))
    +        row = r.fetchone()
    +        if row is not None:
    +            # this rule exists already
    +
    +            if row['state'] == state:
    +                return
    +            # goddamn, same rule - different state, delete old first
    +            self._set(privilege, obj, 0)
    +        # insert new rule
    +
    +        self.ctx.engine.execute(acl_mapping.insert(),
    +            priv_id = priv_id,
    +            subject_id = self.subject.subject_id,
    +            object_id = obj.object_id,
    +            state = state
    +
    +        )
    +
    +    def _bootstrap(self):
    +        """This method is automatically called when subject_id is
    +        None and an subject_id is required."""
    +        r = self.ctx.engine.execute(acl_subjects.insert(),
    +            subject_type = self._type
    +
    +        )
    +        self.subject.subject_id = r.last_inserted_ids()[0]
    +        self.subject.save()
    +
    +    def _bootstrap_object(self, obj):
    +        """Like _bootstrap but works for objects."""
    +
    +        objtype = self._get_object_type(obj)
    +        r = self.ctx.engine.execute(acl_objects.insert(),
    +            object_type = objtype
    +
    +        )
    +        obj.object_id = r.last_inserted_ids()[0]
    +        obj.save()
    +
    +    def _get_object_type(self, obj):
    +        if isinstance(obj, Forum):
    +            return 'forum'
    +
    +        elif isinstance(obj, Thread):
    +            return 'thread'
    +        elif obj is Site:
    +            return 'site'
    +
    +        raise TypeError('obj isn\'t a forum or thread')
    +
    +    def _get_object_join(self, obj):
    +        """Returns a subjoin for the object id."""
    +
    +        t = self._get_object_type(obj)
    +        if t == 'forum':
    +            return meta.select([forums.c.object_id],
    +                forums.c.forum_id == obj.forum_id
    +
    +            )
    +        elif t == 'thread':
    +            return meta.select([posts.c.object_id],
    +                posts.c.post_id == obj.post_id
    +
    +            )
    +        else:
    +            # XXX: it works ^^
    +            # i really want something like meta.select('0 as group_id')
    +            class Fake(object):
    +                def alias(self, n):
    +                    class _C(object):
    +                        class c(object):
    +                            object_id = 0
    +
    +                    return _C
    +            return Fake()
    +
    +    def _get_subject_join(self):
    +        """Returns a subjoin for the subject id."""
    +
    +        if self._type == 'user':
    +            return meta.select([users.c.subject_id],
    +                users.c.user_id == self.subject.user_id
    +
    +            )
    +        return meta.select([groups.c.subject_id],
    +            groups.c.group_id == self.subject.group_id
    +
    +        )
    +
    +    def _fetch_privilege(self, name):
    +        """Returns the priv_id for the given privilege. If it
    +        doesn\'t exist by now the system will create a new
    +        privilege."""
    +        r = self.ctx.engine.execute(meta.select([privileges.c.priv_id],
    +            privileges.c.name == name
    +
    +        ))
    +        row = r.fetchone()
    +        if row is not None:
    +            return row[0]
    +        r = self.ctx.engine.execute(privileges.insert(),
    +            name = name
    +
    +        )
    +        return r.last_inserted_ids()[0]
    +
    +    def __repr__(self):
    +        if self._type == 'user':
    +            id_ = self.subject.user_id
    +
    +        else:
    +            id_ = self.subject.group_id
    +        if self.subject.subject_id is None:
    +            return '<%s %s:%d inactive>' % (
    +                self.__class__.__name__,
    +                self._type,
    +                id_
    +
    +            )
    +        return '<%s %s:%d active as %d>' % (
    +            self.__class__.__name__,
    +            self._type,
    +            id_,
    +            self.subject.subject_id
    +
    +        )
    +
    diff --git a/tests/examplefiles/test.java b/tests/examplefiles/test.java
    new file mode 100644
    index 0000000..64c0853
    --- /dev/null
    +++ b/tests/examplefiles/test.java
    @@ -0,0 +1,653 @@
    +/*
    + * Created on 13-Mar-2004
    + * Created by James Yeh
    + * Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
    + *
    + * 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.
    + * 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.
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    + * 
    + * AELITIS, SAS au capital de 46,603.30 euros
    + * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
    + *
    + */
    +
    +package org.gudy.azureus2.platform.macosx;
    +
    +import org.gudy.azureus2.core3.logging.*;
    +import org.gudy.azureus2.core3.util.AEMonitor;
    +import org.gudy.azureus2.core3.util.Debug;
    +import org.gudy.azureus2.core3.util.SystemProperties;
    +import org.gudy.azureus2.platform.PlatformManager;
    +import org.gudy.azureus2.platform.PlatformManagerCapabilities;
    +import org.gudy.azureus2.platform.PlatformManagerListener;
    +import org.gudy.azureus2.platform.macosx.access.jnilib.OSXAccess;
    +
    +import org.gudy.azureus2.plugins.platform.PlatformManagerException;
    +
    +import java.io.BufferedReader;
    +import java.io.File;
    +import java.io.IOException;
    +import java.io.InputStreamReader;
    +import java.text.MessageFormat;
    +import java.util.HashSet;
    +
    +
    +/**
    + * Performs platform-specific operations with Mac OS X
    + *
    + * @author James Yeh
    + * @version 1.0 Initial Version
    + * @see PlatformManager
    + */
    +public class PlatformManagerImpl implements PlatformManager
    +{
    +    private static final LogIDs LOGID = LogIDs.CORE;
    +
    +    protected static PlatformManagerImpl singleton;
    +    protected static AEMonitor class_mon = new AEMonitor("PlatformManager");
    +
    +    private static final String USERDATA_PATH = new File(System.getProperty("user.home") + "/Library/Application Support/").getPath();
    +
    +    //T: PlatformManagerCapabilities
    +    private final HashSet capabilitySet = new HashSet();
    +
    +    /**
    +     * Gets the platform manager singleton, which was already initialized
    +     */
    +    public static PlatformManagerImpl getSingleton()
    +    {
    +        return singleton;
    +    }
    +
    +    /**
    +     * Tries to enable cocoa-java access and instantiates the singleton
    +     */
    +    static
    +    {
    +        initializeSingleton();
    +    }
    +
    +    /**
    +     * Instantiates the singleton
    +     */
    +    private static void initializeSingleton()
    +    {
    +        try
    +        {
    +            class_mon.enter();
    +            singleton = new PlatformManagerImpl();
    +        }
    +        catch (Throwable e)
    +        {
    +        	Logger.log(new LogEvent(LOGID, "Failed to initialize platform manager"
    +					+ " for Mac OS X", e));
    +        }
    +        finally
    +        {
    +            class_mon.exit();
    +        }
    +    }
    +
    +    /**
    +     * Creates a new PlatformManager and initializes its capabilities
    +     */
    +    public PlatformManagerImpl()
    +    {
    +        capabilitySet.add(PlatformManagerCapabilities.RecoverableFileDelete);
    +        capabilitySet.add(PlatformManagerCapabilities.ShowFileInBrowser);
    +        capabilitySet.add(PlatformManagerCapabilities.ShowPathInCommandLine);
    +        capabilitySet.add(PlatformManagerCapabilities.CreateCommandLineProcess);
    +        capabilitySet.add(PlatformManagerCapabilities.GetUserDataDirectory);
    +        capabilitySet.add(PlatformManagerCapabilities.UseNativeScripting);
    +        capabilitySet.add(PlatformManagerCapabilities.PlaySystemAlert);
    +        
    +        if (OSXAccess.isLoaded()) {
    +	        capabilitySet.add(PlatformManagerCapabilities.GetVersion);
    +        }
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    public int getPlatformType()
    +    {
    +        return PT_MACOSX;
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    public String getVersion() throws PlatformManagerException
    +    {
    +    	if (!OSXAccess.isLoaded()) {
    +        throw new PlatformManagerException("Unsupported capability called on platform manager");
    +    	}
    +    	
    +    	return OSXAccess.getVersion();
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     * @see org.gudy.azureus2.core3.util.SystemProperties#getUserPath()
    +     */
    +    public String getUserDataDirectory() throws PlatformManagerException
    +    {
    +        return USERDATA_PATH;
    +    }
    +
    +	public File
    +	getLocation(
    +		long	location_id )
    +	
    +		throws PlatformManagerException
    +	{
    +		if ( location_id == LOC_USER_DATA ){
    +			
    +			return( new File( USERDATA_PATH ));
    +		}
    +		
    +		return( null );
    +	}
    +    /**
    +     * Not implemented; returns True
    +     */
    +    public boolean isApplicationRegistered() throws PlatformManagerException
    +    {
    +        return true;
    +    }
    +
    +    
    +	public String
    +	getApplicationCommandLine()
    +		throws PlatformManagerException
    +	{
    +		try{	    
    +			String	bundle_path = System.getProperty("user.dir") +SystemProperties.SEP+ SystemProperties.getApplicationName() + ".app";
    +
    +			File osx_app_bundle = new File( bundle_path ).getAbsoluteFile();
    +			
    +			if( !osx_app_bundle.exists() ) {
    +				String msg = "OSX app bundle not found: [" +osx_app_bundle.toString()+ "]";
    +				System.out.println( msg );
    +				if (Logger.isEnabled())
    +					Logger.log(new LogEvent(LOGID, msg));		
    +				throw new PlatformManagerException( msg );
    +			}
    +			
    +			return "open -a \"" +osx_app_bundle.toString()+ "\"";
    +			//return osx_app_bundle.toString() +"/Contents/MacOS/JavaApplicationStub";
    +			
    +		}
    +		catch( Throwable t ){	
    +			t.printStackTrace();
    +			return null;
    +		}
    +	}
    +	
    +	
    +	public boolean
    +	isAdditionalFileTypeRegistered(
    +		String		name,				// e.g. "BitTorrent"
    +		String		type )				// e.g. ".torrent"
    +	
    +		throws PlatformManagerException
    +	{
    +	    throw new PlatformManagerException("Unsupported capability called on platform manager");
    +	}
    +	
    +	public void
    +	unregisterAdditionalFileType(
    +		String		name,				// e.g. "BitTorrent"
    +		String		type )				// e.g. ".torrent"
    +		
    +		throws PlatformManagerException
    +	{
    +		throw new PlatformManagerException("Unsupported capability called on platform manager");
    +	}
    +	
    +	public void
    +	registerAdditionalFileType(
    +		String		name,				// e.g. "BitTorrent"
    +		String		description,		// e.g. "BitTorrent File"
    +		String		type,				// e.g. ".torrent"
    +		String		content_type )		// e.g. "application/x-bittorrent"
    +	
    +		throws PlatformManagerException
    +	{
    +	   throw new PlatformManagerException("Unsupported capability called on platform manager");
    +	}
    +	
    +    /**
    +     * Not implemented; does nothing
    +     */
    +    public void registerApplication() throws PlatformManagerException
    +    {
    +        // handled by LaunchServices and/0r user interaction
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    public void createProcess(String cmd, boolean inheritsHandles) throws PlatformManagerException
    +    {
    +        try
    +        {
    +            performRuntimeExec(cmd.split(" "));
    +        }
    +        catch (Throwable e)
    +        {
    +            throw new PlatformManagerException("Failed to create process", e);
    +        }
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    public void performRecoverableFileDelete(String path) throws PlatformManagerException
    +    {
    +        File file = new File(path);
    +        if(!file.exists())
    +        {
    +	        	if (Logger.isEnabled())
    +							Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Cannot find "
    +									+ file.getName()));
    +            return;
    +        }
    +
    +        boolean useOSA = !NativeInvocationBridge.sharedInstance().isEnabled() || !NativeInvocationBridge.sharedInstance().performRecoverableFileDelete(file);
    +
    +        if(useOSA)
    +        {
    +            try
    +            {
    +                StringBuffer sb = new StringBuffer();
    +                sb.append("tell application \"");
    +                sb.append("Finder");
    +                sb.append("\" to move (posix file \"");
    +                sb.append(path);
    +                sb.append("\" as alias) to the trash");
    +
    +                performOSAScript(sb);
    +            }
    +            catch (Throwable e)
    +            {
    +                throw new PlatformManagerException("Failed to move file", e);
    +            }
    +        }
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    public boolean hasCapability(PlatformManagerCapabilities capability)
    +    {
    +        return capabilitySet.contains(capability);
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    public void dispose()
    +    {
    +        NativeInvocationBridge.sharedInstance().dispose();
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    public void setTCPTOSEnabled(boolean enabled) throws PlatformManagerException
    +    {
    +        throw new PlatformManagerException("Unsupported capability called on platform manager");
    +    }
    +
    +	public void
    +    copyFilePermissions(
    +		String	from_file_name,
    +		String	to_file_name )
    +	
    +		throws PlatformManagerException
    +	{
    +	    throw new PlatformManagerException("Unsupported capability called on platform manager");		
    +	}
    +	
    +    /**
    +     * {@inheritDoc}
    +     */
    +    public void showFile(String path) throws PlatformManagerException
    +    {
    +        File file = new File(path);
    +        if(!file.exists())
    +        {
    +        	if (Logger.isEnabled())
    +        		Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Cannot find "
    +        				+ file.getName()));
    +            throw new PlatformManagerException("File not found");
    +        }
    +
    +        showInFinder(file);
    +    }
    +
    +    // Public utility methods not shared across the interface
    +
    +    /**
    +     * Plays the system alert (the jingle is specified by the user in System Preferences)
    +     */
    +    public void playSystemAlert()
    +    {
    +        try
    +        {
    +            performRuntimeExec(new String[]{"beep"});
    +        }
    +        catch (IOException e)
    +        {
    +        	if (Logger.isEnabled())
    +        		Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
    +						"Cannot play system alert"));
    +        	Logger.log(new LogEvent(LOGID, "", e));
    +        }
    +    }
    +
    +    /**
    +     * 

    Shows the given file or directory in Finder

    + * @param path Absolute path to the file or directory + */ + public void showInFinder(File path) + { + boolean useOSA = !NativeInvocationBridge.sharedInstance().isEnabled() || !NativeInvocationBridge.sharedInstance().showInFinder(path); + + if(useOSA) + { + StringBuffer sb = new StringBuffer(); + sb.append("tell application \""); + sb.append(getFileBrowserName()); + sb.append("\" to reveal (posix file \""); + sb.append(path); + sb.append("\" as alias)"); + + try + { + performOSAScript(sb); + } + catch (IOException e) + { + Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, e + .getMessage())); + } + } + } + + /** + *

    Shows the given file or directory in Terminal by executing cd /absolute/path/to

    + * @param path Absolute path to the file or directory + */ + public void showInTerminal(String path) + { + showInTerminal(new File(path)); + } + + /** + *

    Shows the given file or directory in Terminal by executing cd /absolute/path/to

    + * @param path Absolute path to the file or directory + */ + public void showInTerminal(File path) + { + if (path.isFile()) + { + path = path.getParentFile(); + } + + if (path != null && path.isDirectory()) + { + StringBuffer sb = new StringBuffer(); + sb.append("tell application \""); + sb.append("Terminal"); + sb.append("\" to do script \"cd "); + sb.append(path.getAbsolutePath().replaceAll(" ", "\\ ")); + sb.append("\""); + + try + { + performOSAScript(sb); + } + catch (IOException e) + { + Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, e + .getMessage())); + } + } + else + { + if (Logger.isEnabled()) + Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Cannot find " + + path.getName())); + } + } + + // Internal utility methods + + /** + * Compiles a new AppleScript instance and runs it + * @param cmd AppleScript command to execute; do not surround command with extra quotation marks + * @return Output of the script + * @throws IOException If the script failed to execute + */ + protected static String performOSAScript(CharSequence cmd) throws IOException + { + return performOSAScript(new CharSequence[]{cmd}); + } + + /** + * Compiles a new AppleScript instance and runs it + * @param cmds AppleScript Sequence of commands to execute; do not surround command with extra quotation marks + * @return Output of the script + * @throws IOException If the script failed to execute + */ + protected static String performOSAScript(CharSequence[] cmds) throws IOException + { + long start = System.currentTimeMillis(); + Debug.outNoStack("Executing OSAScript: "); + for (int i = 0; i < cmds.length; i++) + { + Debug.outNoStack("\t" + cmds[i]); + } + + String[] cmdargs = new String[2 * cmds.length + 1]; + cmdargs[0] = "osascript"; + for (int i = 0; i < cmds.length; i++) + { + cmdargs[i * 2 + 1] = "-e"; + cmdargs[i * 2 + 2] = String.valueOf(cmds[i]); + } + + Process osaProcess = performRuntimeExec(cmdargs); + BufferedReader reader = new BufferedReader(new InputStreamReader(osaProcess.getInputStream())); + String line = reader.readLine(); + reader.close(); + Debug.outNoStack("OSAScript Output: " + line); + + reader = new BufferedReader(new InputStreamReader(osaProcess.getErrorStream())); + String errorMsg = reader.readLine(); + reader.close(); + + Debug.outNoStack("OSAScript Error (if any): " + errorMsg); + + Debug.outNoStack(MessageFormat.format("OSAScript execution ended ({0}ms)", new Object[]{String.valueOf(System.currentTimeMillis() - start)})); + + if (errorMsg != null) + { + throw new IOException(errorMsg); + } + + return line; + } + + /** + * Compiles a new AppleScript instance and runs it + * @param script AppleScript file (.scpt) to execute + * @return Output of the script + * @throws IOException If the script failed to execute + */ + protected static String performOSAScript(File script) throws IOException + { + long start = System.currentTimeMillis(); + Debug.outNoStack("Executing OSAScript from file: " + script.getPath()); + + Process osaProcess = performRuntimeExec(new String[]{"osascript", script.getPath()}); + BufferedReader reader = new BufferedReader(new InputStreamReader(osaProcess.getInputStream())); + String line = reader.readLine(); + reader.close(); + Debug.outNoStack("OSAScript Output: " + line); + + reader = new BufferedReader(new InputStreamReader(osaProcess.getErrorStream())); + String errorMsg = reader.readLine(); + reader.close(); + + Debug.outNoStack("OSAScript Error (if any): " + errorMsg); + + Debug.outNoStack(MessageFormat.format("OSAScript execution ended ({0}ms)", new Object[]{String.valueOf(System.currentTimeMillis() - start)})); + + if (errorMsg != null) + { + throw new IOException(errorMsg); + } + + return line; + } + + /** + * Compiles a new AppleScript instance to the specified location + * @param cmd Command to compile; do not surround command with extra quotation marks + * @param destination Destination location of the AppleScript file + * @return True if compiled successfully + */ + protected static boolean compileOSAScript(CharSequence cmd, File destination) + { + return compileOSAScript(new CharSequence[]{cmd}, destination); + } + + /** + * Compiles a new AppleScript instance to the specified location + * @param cmds Sequence of commands to compile; do not surround command with extra quotation marks + * @param destination Destination location of the AppleScript file + * @return True if compiled successfully + */ + protected static boolean compileOSAScript(CharSequence[] cmds, File destination) + { + long start = System.currentTimeMillis(); + Debug.outNoStack("Compiling OSAScript: " + destination.getPath()); + for (int i = 0; i < cmds.length; i++) + { + Debug.outNoStack("\t" + cmds[i]); + } + + String[] cmdargs = new String[2 * cmds.length + 3]; + cmdargs[0] = "osacompile"; + for (int i = 0; i < cmds.length; i++) + { + cmdargs[i * 2 + 1] = "-e"; + cmdargs[i * 2 + 2] = String.valueOf(cmds[i]); + } + + cmdargs[cmdargs.length - 2] = "-o"; + cmdargs[cmdargs.length - 1] = destination.getPath(); + + String errorMsg; + try + { + Process osaProcess = performRuntimeExec(cmdargs); + + BufferedReader reader = new BufferedReader(new InputStreamReader(osaProcess.getErrorStream())); + errorMsg = reader.readLine(); + reader.close(); + } + catch (IOException e) + { + Debug.outNoStack("OSACompile Execution Failed: " + e.getMessage()); + Debug.printStackTrace(e); + return false; + } + + Debug.outNoStack("OSACompile Error (if any): " + errorMsg); + + Debug.outNoStack(MessageFormat.format("OSACompile execution ended ({0}ms)", new Object[]{String.valueOf(System.currentTimeMillis() - start)})); + + return (errorMsg == null); + } + + /** + * @see Runtime#exec(String[]) + */ + protected static Process performRuntimeExec(String[] cmdargs) throws IOException + { + try + { + return Runtime.getRuntime().exec(cmdargs); + } + catch (IOException e) + { + Logger.log(new LogAlert(LogAlert.UNREPEATABLE, e.getMessage(), e)); + throw e; + } + } + + /** + *

    Gets the preferred file browser name

    + *

    Currently supported browsers are Path Finder and Finder. If Path Finder is currently running + * (not just installed), then "Path Finder is returned; else, "Finder" is returned.

    + * @return "Path Finder" if it is currently running; else "Finder" + */ + private static String getFileBrowserName() + { + try + { + // slowwwwwwww + if ("true".equalsIgnoreCase(performOSAScript("tell application \"System Events\" to exists process \"Path Finder\""))) + { + Debug.outNoStack("Path Finder is running"); + + return "Path Finder"; + } + else + { + return "Finder"; + } + } + catch (IOException e) + { + Debug.printStackTrace(e); + Logger.log(new LogEvent(LOGID, e.getMessage(), e)); + + return "Finder"; + } + } + + public boolean + testNativeAvailability( + String name ) + + throws PlatformManagerException + { + throw new PlatformManagerException("Unsupported capability called on platform manager"); + } + + public void + addListener( + PlatformManagerListener listener ) + { + } + + public void + removeListener( + PlatformManagerListener listener ) + { + } +} diff --git a/tests/examplefiles/test.jsp b/tests/examplefiles/test.jsp new file mode 100644 index 0000000..1c6664d --- /dev/null +++ b/tests/examplefiles/test.jsp @@ -0,0 +1,24 @@ + +<%= var x = 1; +%> +<%! int i = 0; %> +<%! int a, b, c; %> +<%! Circle a = new Circle(2.0); %> + +<% + String name = null; + if (request.getParameter("name") == null) { +%> +<%@ include file="error.html" %> +<% + } else { + foo.setName(request.getParameter("name")); + if (foo.getName().equalsIgnoreCase("integra")) + name = "acura"; + if (name.equalsIgnoreCase( "acura" )) { +%> + + +

    +Calendar of +

    diff --git a/tests/examplefiles/test.moo b/tests/examplefiles/test.moo new file mode 100644 index 0000000..dec71ba --- /dev/null +++ b/tests/examplefiles/test.moo @@ -0,0 +1,51 @@ +you_lose_msg = "Either that person does not exist, or has a different password."; +if (!(caller in {#0, this})) + return E_PERM; + "...caller isn't :do_login_command..."; +elseif (args && (args[1] == "test")) + return this:test(@listdelete(args, 1)); +elseif (!(length(args) in {1, 2})) + notify(player, tostr("Usage: ", verb, " ")); +elseif (!valid(candidate = this:_match_player(name = strsub(args[1], " ", "_")))) + if (name == "guest") + "must be no guests"; + this:notify_lines(this:registration_text("guest")); + else + notify(player, you_lose_msg); + endif + "...unknown player..."; +elseif (is_clear_property(candidate, "password") || ((typeof(candidate.password) == STR) && ((length(candidate.password) < 2) || (crypt({@args, ""}[2], candidate.password) != candidate.password)))) + notify(player, you_lose_msg); + "...bad password..."; + server_log(tostr("FAILED CONNECT: ", args[1], " (", candidate, ") on ", connection_name(player), ($string_utils:connection_hostname(connection_name(player)) in candidate.all_connect_places) ? "" | "******")); +elseif (((candidate.name == "guest") && this.sitematch_guests) && valid(foreigner = $country_db:get_guest())) + notify(player, tostr("Okay,... Logging you in as `", foreigner:name(), "'")); + this:record_connection(foreigner); + return foreigner; +elseif ((parent(candidate) == $guest) && (!valid(candidate = candidate:defer()))) + if (candidate == #-3) + notify(player, "Sorry, guest characters are not allowed from your site right now."); + elseif (candidate == #-2) + this:notify_lines(this:registration_text("blacklisted", "Sorry, guest characters are not allowed from your site.")); + elseif (candidate == #-4) + this:notify_lines(this:registration_text("guest")); + else + notify(player, "Sorry, all of our guest characters are in use right now."); + endif +else + if ((!(name in candidate.aliases)) && (name != tostr(candidate))) + notify(player, tostr("Okay,... ", name, " is in use. Logging you in as `", candidate:name(), "'")); + endif + if (this:is_newted(candidate)) + notify(player, ""); + notify(player, this:newt_message_for(candidate)); + notify(player, ""); + else + this:record_connection(candidate); + if (verb[1] == "s") + candidate.use_do_command = 0; + endif + return candidate; + endif +endif +return 0; \ No newline at end of file diff --git a/tests/examplefiles/test.myt b/tests/examplefiles/test.myt new file mode 100644 index 0000000..1668f7a --- /dev/null +++ b/tests/examplefiles/test.myt @@ -0,0 +1,166 @@ +<%doc>formatting.myt - Provides section formatting elements, syntax-highlighted code blocks, and other special filters. + +<%global> + import string, re + import highlight + + +<%method section> +<%doc>Main section formatting element. +<%args> + toc + path + description=None + onepage=False + +<%init> + item = toc.get_by_path(path) + if item is None: + raise "path: " + path + + + + +
    + +<%python> + content = m.content() + re2 = re.compile(r"'''PYESC(.+?)PYESC'''", re.S) + content = re2.sub(lambda m: m.group(1), content) + + +% if item.depth > 1: +

    <% description or item.description %>

    +% + +
    + <% content %> +
    + +% if onepage or item.depth > 1: +% if (item.next and item.next.depth >= item.depth): + back to section top +% +% else: + back to section top + <& nav.myt:pagenav, item=item, onepage=onepage &> +% +
    + + + + +<%method formatplain> + <%filter> + import re + f = re.sub(r'\n[\s\t]*\n[\s\t]*', '

    \n

    ', f) + f = "

    " + f + "

    " + return f + +<% m.content() | h%> + + + + + +<%method codeline trim="both"> +<% m.content() %> + + +<%method code autoflush=False> +<%args> + title = None + syntaxtype = 'python' + html_escape = False + use_sliders = False + + +<%init> + def fix_indent(f): + f =string.expandtabs(f, 4) + g = '' + lines = string.split(f, "\n") + whitespace = None + for line in lines: + if whitespace is None: + match = re.match(r"^([ ]*).+", line) + if match is not None: + whitespace = match.group(1) + + if whitespace is not None: + line = re.sub(r"^%s" % whitespace, "", line) + + if whitespace is not None or re.search(r"\w", line) is not None: + g += (line + "\n") + + + return g.rstrip() + + p = re.compile(r'
    (.*?)
    ', re.S) + def hlight(match): + return "
    " + highlight.highlight(fix_indent(match.group(1)), html_escape = html_escape, syntaxtype = syntaxtype) + "
    " + content = p.sub(hlight, "
    " + m.content() + "
    ") + +
    "> +% if title is not None: +
    <% title %>
    +% +<% content %>
    + + + + + +<%method popboxlink trim="both"> + <%args> + name=None + show='show' + hide='hide' + + <%init> + if name is None: + name = m.attributes.setdefault('popbox_name', 0) + name += 1 + m.attributes['popbox_name'] = name + name = "popbox_" + repr(name) + +javascript:togglePopbox('<% name %>', '<% show %>', '<% hide %>') + + +<%method popbox trim="both"> +<%args> + name = None + class_ = None + +<%init> + if name is None: + name = 'popbox_' + repr(m.attributes['popbox_name']) + + + + +<%method poplink trim="both"> + <%args> + link='sql' + + <%init> + href = m.scomp('SELF:popboxlink') + + '''PYESC<& nav.myt:link, href=href, text=link, class_="codepoplink" &>PYESC''' + + +<%method codepopper trim="both"> + <%init> + c = m.content() + c = re.sub(r'\n', '
    \n', c.strip()) + +
    <&|SELF:popbox, class_="codepop" &><% c %>
    +
    +
    +<%method poppedcode trim="both">
    +	<%init>
    +		c = m.content()
    +		c = re.sub(r'\n', '
    \n', c.strip()) + +
    <% c %>
    +
    diff --git a/tests/examplefiles/test.pas b/tests/examplefiles/test.pas
    new file mode 100644
    index 0000000..2724bbf
    --- /dev/null
    +++ b/tests/examplefiles/test.pas
    @@ -0,0 +1,743 @@
    +//
    +// Sourcecode from http://www.delphi-library.de/topic_47880.html
    +//
    +uses Windows, Messages;
    +
    +const
    +  FFM_INIT               = WM_USER + 1976;
    +  FFM_ONFILEFOUND        = WM_USER + 1974; // wParam: not used, lParam: Filename
    +  FFM_ONDIRFOUND         = WM_USER + 1975; // wParam: NumFolder, lParam: Directory
    +var
    +  CntFolders             : Cardinal = 0;
    +  NumFolder              : Cardinal = 0;
    +
    +
    +////////////////////////////////////////////////////////////////////////////////
    +//
    +//  FindAllFilesInit
    +//
    +//
    +procedure FindAllFilesInit; external;
    +label foo;
    +begin
    +  CntFolders := 0;
    +  NumFolder := 0;
    +foo:
    +  Blub;
    +  goto foo;
    +end;
    +
    +////////////////////////////////////////////////////////////////////////////////
    +//
    +//  CountFolders
    +//
    +//
    +procedure CountFolders(Handle: THandle; RootFolder: string; Recurse: Boolean = True);
    +var
    +  hFindFile              : THandle;
    +  wfd                    : TWin32FindData;
    +begin
    +  SendMessage(Handle, FFM_INIT, 0, 0);
    +  if RootFolder[length(RootFolder)] <> '\' then
    +    RootFolder := RootFolder + '\';
    +  ZeroMemory(@wfd, sizeof(wfd));
    +  wfd.dwFileAttributes := FILE_ATTRIBUTE_NORMAL;
    +  if Recurse then
    +  begin
    +    hFindFile := FindFirstFile(pointer(RootFolder + '*.*'), wfd);
    +    if hFindFile <> 0 then
    +    try
    +      repeat
    +        if wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = FILE_ATTRIBUTE_DIRECTORY then
    +        begin
    +          if (string(wfd.cFileName) <> '.') and (string(wfd.cFileName) <> '..') then
    +          begin
    +            CountFolders(Handle, RootFolder + wfd.cFileName, Recurse);
    +          end;
    +        end;
    +      until FindNextFile(hFindFile, wfd) = False;
    +      Inc(CntFolders);
    +    finally
    +      Windows.FindClose(hFindFile);
    +    end;
    +  end;
    +end;
    +
    +////////////////////////////////////////////////////////////////////////////////
    +//
    +//  FindAllFiles
    +//
    +procedure FindAllFiles(Handle: THandle; RootFolder: string; Mask: string; Recurse: Boolean = True);
    +var
    +  hFindFile              : THandle;
    +  wfd                    : TWin32FindData;
    +begin
    +  if RootFolder[length(RootFolder)] <> '\' then
    +    RootFolder := RootFolder + '\';
    +  ZeroMemory(@wfd, sizeof(wfd));
    +  wfd.dwFileAttributes := FILE_ATTRIBUTE_NORMAL;
    +  if Recurse then
    +  begin
    +    hFindFile := FindFirstFile(pointer(RootFolder + '*.*'), wfd);
    +    if hFindFile <> 0 then
    +    try
    +      repeat
    +        if wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = FILE_ATTRIBUTE_DIRECTORY then
    +        begin
    +          if (string(wfd.cFileName) <> '.') and (string(wfd.cFileName) <> '..') then
    +          begin
    +            FindAllFiles(Handle, RootFolder + wfd.cFileName, Mask, Recurse);
    +          end;
    +        end;
    +      until FindNextFile(hFindFile, wfd) = False;
    +      Inc(NumFolder);
    +      SendMessage(Handle, FFM_ONDIRFOUND, NumFolder, lParam(string(RootFolder)));
    +    finally
    +      Windows.FindClose(hFindFile);
    +    end;
    +  end;
    +  hFindFile := FindFirstFile(pointer(RootFolder + Mask), wfd);
    +  if hFindFile <> INVALID_HANDLE_VALUE then
    +  try
    +    repeat
    +      if (wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> FILE_ATTRIBUTE_DIRECTORY) then
    +      begin
    +        SendMessage(Handle, FFM_ONFILEFOUND, 0, lParam(string(RootFolder + wfd.cFileName)));
    +      end;
    +    until FindNextFile(hFindFile, wfd) = False;
    +  finally
    +    Windows.FindClose(hFindFile);
    +  end;
    +end;
    +
    +
    +property test: boolean read ftest write ftest;
    +procedure test: boolean read ftest write ftest;
    +
    +//
    +// This sourcecode is part of omorphia
    +//
    +
    +Function IsValidHandle(Const Handle: THandle): Boolean; {$IFDEF OMORPHIA_FEATURES_USEASM} Assembler;
    +Asm
    +    TEST    EAX, EAX
    +    JZ      @@Finish
    +    NOT     EAX
    +    TEST    EAX, EAX
    +    SETNZ   AL
    +
    +    {$IFDEF WINDOWS}
    +    JZ      @@Finish
    +
    +    //Save the handle against modifications or loss
    +    PUSH    EAX
    +
    +    //reserve some space for a later duplicate
    +    PUSH    EAX
    +
    +    //Check if we are working on NT-Platform
    +    CALL    IsWindowsNTSystem
    +    TEST    EAX, EAX
    +    JZ      @@NoNTSystem
    +
    +    PUSH    DWORD PTR [ESP]
    +    LEA     EAX, DWORD PTR [ESP+$04]
    +    PUSH    EAX
    +    CALL    GetHandleInformation
    +    TEST    EAX, EAX
    +    JNZ     @@Finish2
    +
    +@@NoNTSystem:
    +    //Result := DuplicateHandle(GetCurrentProcess, Handle, GetCurrentProcess,
    +    //  @Duplicate, 0, False, DUPLICATE_SAME_ACCESS);
    +    PUSH    DUPLICATE_SAME_ACCESS
    +    PUSH    $00000000
    +    PUSH    $00000000
    +    LEA     EAX, DWORD PTR [ESP+$0C]
    +    PUSH    EAX
    +    CALL    GetCurrentProcess
    +    PUSH    EAX
    +    PUSH    DWORD PTR [ESP+$18]
    +    PUSH    EAX
    +    CALL    DuplicateHandle
    +
    +    TEST    EAX, EAX
    +    JZ      @@Finish2
    +
    +    //  Result := CloseHandle(Duplicate);
    +    PUSH    DWORD PTR [ESP]
    +    CALL    CloseHandle
    +
    +@@Finish2:
    +    POP     EDX
    +    POP     EDX
    +
    +    PUSH    EAX
    +    PUSH    $00000000
    +    CALL    SetLastError
    +    POP     EAX
    +    {$ENDIF}
    +
    +@@Finish:
    +End;
    +{$ELSE}
    +Var
    +    Duplicate: THandle;
    +    Flags: DWORD;
    +Begin
    +    If IsWinNT Then
    +        Result := GetHandleInformation(Handle, Flags)
    +    Else
    +        Result := False;
    +    If Not Result Then
    +    Begin
    +        // DuplicateHandle is used as an additional check for those object types not
    +        // supported by GetHandleInformation (e.g. according to the documentation,
    +        // GetHandleInformation doesn't support window stations and desktop although
    +        // tests show that it does). GetHandleInformation is tried first because its
    +        // much faster. Additionally GetHandleInformation is only supported on NT...
    +        Result := DuplicateHandle(GetCurrentProcess, Handle, GetCurrentProcess,
    +            @Duplicate, 0, False, DUPLICATE_SAME_ACCESS);
    +        If Result Then
    +            Result := CloseHandle(Duplicate);
    +    End;
    +End;
    +{$ENDIF}
    +
    +
    +    	
    +
    +{*******************************************************}
    +{                                                       }
    +{       Delphi Supplemental Components                  }
    +{       ZLIB Data Compression Interface Unit            }
    +{                                                       }
    +{       Copyright (c) 1997 Borland International        }
    +{                                                       }
    +{*******************************************************}
    +
    +{ Modified for zlib 1.1.3 by Davide Moretti  Z_STREAM_END do
    +      begin
    +        P := OutBuf;
    +        Inc(OutBytes, 256);
    +        ReallocMem(OutBuf, OutBytes);
    +        strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
    +        strm.avail_out := 256;
    +      end;
    +    finally
    +      CCheck(deflateEnd(strm));
    +    end;
    +    ReallocMem(OutBuf, strm.total_out);
    +    OutBytes := strm.total_out;
    +  except
    +    FreeMem(OutBuf);
    +    raise
    +  end;
    +end;
    +
    +
    +procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
    +  OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer);
    +var
    +  strm: TZStreamRec;
    +  P: Pointer;
    +  BufInc: Integer;
    +begin
    +  FillChar(strm, sizeof(strm), 0);
    +  BufInc := (InBytes + 255) and not 255;
    +  if OutEstimate = 0 then
    +    OutBytes := BufInc
    +  else
    +    OutBytes := OutEstimate;
    +  GetMem(OutBuf, OutBytes);
    +  try
    +    strm.next_in := InBuf;
    +    strm.avail_in := InBytes;
    +    strm.next_out := OutBuf;
    +    strm.avail_out := OutBytes;
    +    DCheck(inflateInit_(strm, zlib_version, sizeof(strm)));
    +    try
    +      while DCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END do
    +      begin
    +        P := OutBuf;
    +        Inc(OutBytes, BufInc);
    +        ReallocMem(OutBuf, OutBytes);
    +        strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
    +        strm.avail_out := BufInc;
    +      end;
    +    finally
    +      DCheck(inflateEnd(strm));
    +    end;
    +    ReallocMem(OutBuf, strm.total_out);
    +    OutBytes := strm.total_out;
    +  except
    +    FreeMem(OutBuf);
    +    raise
    +  end;
    +end;
    +
    +
    +// TCustomZlibStream
    +
    +constructor TCustomZLibStream.Create(Strm: TStream);
    +begin
    +  inherited Create;
    +  FStrm := Strm;
    +  FStrmPos := Strm.Position;
    +end;
    +
    +procedure TCustomZLibStream.Progress(Sender: TObject);
    +begin
    +  if Assigned(FOnProgress) then FOnProgress(Sender);
    +end;
    +
    +
    +// TCompressionStream
    +
    +constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel;
    +  Dest: TStream);
    +const
    +  Levels: array [TCompressionLevel] of ShortInt =
    +    (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
    +begin
    +  inherited Create(Dest);
    +  FZRec.next_out := FBuffer;
    +  FZRec.avail_out := sizeof(FBuffer);
    +  CCheck(deflateInit_(FZRec, Levels[CompressionLevel], zlib_version, sizeof(FZRec)));
    +end;
    +
    +destructor TCompressionStream.Destroy;
    +begin
    +  FZRec.next_in := nil;
    +  FZRec.avail_in := 0;
    +  try
    +    if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
    +    while (CCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END)
    +      and (FZRec.avail_out = 0) do
    +    begin
    +      FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
    +      FZRec.next_out := FBuffer;
    +      FZRec.avail_out := sizeof(FBuffer);
    +    end;
    +    if FZRec.avail_out < sizeof(FBuffer) then
    +      FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out);
    +  finally
    +    deflateEnd(FZRec);
    +  end;
    +  inherited Destroy;
    +end;
    +
    +function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
    +begin
    +  raise ECompressionError.Create('Invalid stream operation');
    +end;
    +
    +function TCompressionStream.Write(const Buffer; Count: Longint): Longint;
    +begin
    +  FZRec.next_in := @Buffer;
    +  FZRec.avail_in := Count;
    +  if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
    +  while (FZRec.avail_in > 0) do
    +  begin
    +    CCheck(deflate(FZRec, 0));
    +    if FZRec.avail_out = 0 then
    +    begin
    +      FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
    +      FZRec.next_out := FBuffer;
    +      FZRec.avail_out := sizeof(FBuffer);
    +      FStrmPos := FStrm.Position;
    +      Progress(Self);
    +    end;
    +  end;
    +  Result := Count;
    +end;
    +
    +function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
    +begin
    +  if (Offset = 0) and (Origin = soFromCurrent) then
    +    Result := FZRec.total_in
    +  else
    +    raise ECompressionError.Create('Invalid stream operation');
    +end;
    +
    +function TCompressionStream.GetCompressionRate: Single;
    +begin
    +  if FZRec.total_in = 0 then
    +    Result := 0
    +  else
    +    Result := (1.0 - (FZRec.total_out / FZRec.total_in)) * 100.0;
    +end;
    +
    +
    +// TDecompressionStream
    +
    +constructor TDecompressionStream.Create(Source: TStream);
    +begin
    +  inherited Create(Source);
    +  FZRec.next_in := FBuffer;
    +  FZRec.avail_in := 0;
    +  DCheck(inflateInit_(FZRec, zlib_version, sizeof(FZRec)));
    +end;
    +
    +destructor TDecompressionStream.Destroy;
    +begin
    +  inflateEnd(FZRec);
    +  inherited Destroy;
    +end;
    +
    +function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
    +begin
    +  FZRec.next_out := @Buffer;
    +  FZRec.avail_out := Count;
    +  if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
    +  while (FZRec.avail_out > 0) do
    +  begin
    +    if FZRec.avail_in = 0 then
    +    begin
    +      FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer));
    +      if FZRec.avail_in = 0 then
    +        begin
    +          Result := Count - FZRec.avail_out;
    +          Exit;
    +        end;
    +      FZRec.next_in := FBuffer;
    +      FStrmPos := FStrm.Position;
    +      Progress(Self);
    +    end;
    +    DCheck(inflate(FZRec, 0));
    +  end;
    +  Result := Count;
    +end;
    +
    +function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
    +begin
    +  raise EDecompressionError.Create('Invalid stream operation');
    +end;
    +
    +function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
    +var
    +  I: Integer;
    +  Buf: array [0..4095] of Char;
    +begin
    +  if (Offset = 0) and (Origin = soFromBeginning) then
    +  begin
    +    DCheck(inflateReset(FZRec));
    +    FZRec.next_in := FBuffer;
    +    FZRec.avail_in := 0;
    +    FStrm.Position := 0;
    +    FStrmPos := 0;
    +  end
    +  else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
    +          ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then
    +  begin
    +    if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
    +    if Offset > 0 then
    +    begin
    +      for I := 1 to Offset div sizeof(Buf) do
    +        ReadBuffer(Buf, sizeof(Buf));
    +      ReadBuffer(Buf, Offset mod sizeof(Buf));
    +    end;
    +  end
    +  else
    +    raise EDecompressionError.Create('Invalid stream operation');
    +  Result := FZRec.total_out;
    +end;
    +
    +end.
    diff --git a/tests/examplefiles/test.php b/tests/examplefiles/test.php
    new file mode 100644
    index 0000000..90de747
    --- /dev/null
    +++ b/tests/examplefiles/test.php
    @@ -0,0 +1,498 @@
    +
    + *  @copyright   Copyright (c) 2006, Manni
    + *  @version     1.0
    + *  @link        http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
    + *  @link        http://mannithedark.is-a-geek.net/
    + *  @since       1.0
    + *  @package     fnord.bb
    + *  @subpackage  archive
    + */
    +class Zip extends Archive {
    + /**
    +  *  Outputs the zip file
    +  *
    +  *  This function creates the zip file with the dirs and files given.
    +  *  If the optional parameter $file is given, the zip file is will be
    +  *  saved at that location. Otherwise the function returns the zip file's content.
    +  *
    +  *  @access                   public
    +  *
    +  *  @link                     http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
    +  *  @param  string $filename  The path where the zip file will be saved
    +  *
    +  *  @return bool|string       Returns either true if the fil is sucessfully created or the content of the zip file
    +  */
    +  function out($filename = false) {
    +    // Empty output
    +    $file_data = array(); // Data of the file part
    +    $cd_data   = array(); // Data of the central directory
    +
    +    // Sort dirs and files by path length
    +    uksort($this->dirs,  'sort_by_length');
    +    uksort($this->files, 'sort_by_length');
    +
    +    // Handle dirs
    +    foreach($this->dirs as $dir) {
    +      $dir .= '/';
    +      // File part
    +
    +      // Reset dir data
    +      $dir_data = '';
    +
    +      // Local file header
    +      $dir_data .= "\x50\x4b\x03\x04";      // Local file header signature
    +      $dir_data .= pack("v", 10);           // Version needed to extract
    +      $dir_data .= pack("v", 0);            // General purpose bit flag
    +      $dir_data .= pack("v", 0);            // Compression method
    +      $dir_data .= pack("v", 0);            // Last mod file time
    +      $dir_data .= pack("v", 0);            // Last mod file date
    +      $dir_data .= pack("V", 0);            // crc-32
    +      $dir_data .= pack("V", 0);            // Compressed size
    +      $dir_data .= pack("V", 0);            // Uncompressed size
    +      $dir_data .= pack("v", strlen($dir)); // File name length
    +      $dir_data .= pack("v", 0);            // Extra field length
    +
    +      $dir_data .= $dir;                    // File name
    +      $dir_data .= '';                      // Extra field (is empty)
    +
    +      // File data
    +      $dir_data .= '';                      // Dirs have no file data
    +
    +      // Data descriptor
    +      $dir_data .= pack("V", 0);            // crc-32
    +      $dir_data .= pack("V", 0);            // Compressed size
    +      $dir_data .= pack("V", 0);            // Uncompressed size
    +
    +      // Save current offset
    +      $offset = strlen(implode('', $file_data));
    +
    +      // Append dir data to the file part
    +      $file_data[] = $dir_data;
    +
    +      // Central directory
    +
    +      // Reset dir data
    +      $dir_data = '';
    +
    +      // File header
    +      $dir_data .= "\x50\x4b\x01\x02";      // Local file header signature
    +      $dir_data .= pack("v", 0);            // Version made by
    +      $dir_data .= pack("v", 10);           // Version needed to extract
    +      $dir_data .= pack("v", 0);            // General purpose bit flag
    +      $dir_data .= pack("v", 0);            // Compression method
    +      $dir_data .= pack("v", 0);            // Last mod file time
    +      $dir_data .= pack("v", 0);            // Last mod file date
    +      $dir_data .= pack("V", 0);            // crc-32
    +      $dir_data .= pack("V", 0);            // Compressed size
    +      $dir_data .= pack("V", 0);            // Uncompressed size
    +      $dir_data .= pack("v", strlen($dir)); // File name length
    +      $dir_data .= pack("v", 0);            // Extra field length
    +      $dir_data .= pack("v", 0);            // File comment length
    +      $dir_data .= pack("v", 0);            // Disk number start
    +      $dir_data .= pack("v", 0);            // Internal file attributes
    +      $dir_data .= pack("V", 16);           // External file attributes
    +      $dir_data .= pack("V", $offset);      // Relative offset of local header
    +
    +      $dir_data .= $dir;                    // File name
    +      $dir_data .= '';                      // Extra field (is empty)
    +      $dir_data .= '';                      // File comment (is empty)
    +
    +      /*
    +      // Data descriptor
    +      $dir_data .= pack("V", 0);            // crc-32
    +      $dir_data .= pack("V", 0);            // Compressed size
    +      $dir_data .= pack("V", 0);            // Uncompressed size
    +      */
    +      
    +      // Append dir data to the central directory data
    +      $cd_data[] = $dir_data;
    +    }
    +
    +    // Handle files
    +    foreach($this->files as $name => $file) {
    +      // Get values
    +      $content = $file[0];
    +    
    +      // File part
    +
    +      // Reset file data
    +      $fd = '';
    +      
    +      // Detect possible compressions
    +      // Use deflate
    +      if(function_exists('gzdeflate')) {
    +        $method = 8;
    +
    +        // Compress file content
    +        $compressed_data = gzdeflate($content);
    +
    +      // Use bzip2
    +      } elseif(function_exists('bzcompress')) {
    +        $method = 12;
    +
    +        // Compress file content
    +        $compressed_data = bzcompress($content);
    +
    +      // No compression
    +      } else {
    +        $method = 0;
    +
    +        // Do not compress the content :P
    +        $compressed_data = $content;
    +      }
    +
    +      // Local file header
    +      $fd .= "\x50\x4b\x03\x04";                  // Local file header signature
    +      $fd .= pack("v", 20);                       // Version needed to extract
    +      $fd .= pack("v", 0);                        // General purpose bit flag
    +      $fd .= pack("v", $method);                  // Compression method
    +      $fd .= pack("v", 0);                        // Last mod file time
    +      $fd .= pack("v", 0);                        // Last mod file date
    +      $fd .= pack("V", crc32($content));          // crc-32
    +      $fd .= pack("V", strlen($compressed_data)); // Compressed size
    +      $fd .= pack("V", strlen($content));         // Uncompressed size
    +      $fd .= pack("v", strlen($name));            // File name length
    +      $fd .= pack("v", 0);                        // Extra field length
    +
    +      $fd .= $name;                               // File name
    +      $fd .= '';                                  // Extra field (is empty)
    +
    +      // File data
    +      $fd .= $compressed_data;
    +      
    +      // Data descriptor
    +      $fd .= pack("V", crc32($content));          // crc-32
    +      $fd .= pack("V", strlen($compressed_data)); // Compressed size
    +      $fd .= pack("V", strlen($content));         // Uncompressed size
    +
    +      // Save current offset
    +      $offset = strlen(implode('', $file_data));
    +
    +      // Append file data to the file part
    +      $file_data[] = $fd;
    +
    +      // Central directory
    +
    +      // Reset file data
    +      $fd = '';
    +
    +      // File header
    +      $fd .= "\x50\x4b\x01\x02";                  // Local file header signature
    +      $fd .= pack("v", 0);                        // Version made by
    +      $fd .= pack("v", 20);                       // Version needed to extract
    +      $fd .= pack("v", 0);                        // General purpose bit flag
    +      $fd .= pack("v", $method);                  // Compression method
    +      $fd .= pack("v", 0);                        // Last mod file time
    +      $fd .= pack("v", 0);                        // Last mod file date
    +      $fd .= pack("V", crc32($content));          // crc-32
    +      $fd .= pack("V", strlen($compressed_data)); // Compressed size
    +      $fd .= pack("V", strlen($content));         // Uncompressed size
    +      $fd .= pack("v", strlen($name));            // File name length
    +      $fd .= pack("v", 0);                        // Extra field length
    +      $fd .= pack("v", 0);                        // File comment length
    +      $fd .= pack("v", 0);                        // Disk number start
    +      $fd .= pack("v", 0);                        // Internal file attributes
    +      $fd .= pack("V", 32);                       // External file attributes
    +      $fd .= pack("V", $offset);                  // Relative offset of local header
    +
    +      $fd .= $name;                               // File name
    +      $fd .= '';                                  // Extra field (is empty)
    +      $fd .= '';                                  // File comment (is empty)
    +
    +      /*
    +      // Data descriptor
    +      $fd .= pack("V", crc32($content));          // crc-32
    +      $fd .= pack("V", strlen($compressed_data)); // Compressed size
    +      $fd .= pack("V", strlen($content));         // Uncompressed size
    +      */
    +
    +      // Append file data to the central directory data
    +      $cd_data[] = $fd;
    +    }
    +
    +    // Digital signature
    +    $digital_signature = '';
    +    $digital_signature .= "\x50\x4b\x05\x05";  // Header signature
    +    $digital_signature .= pack("v", 0);        // Size of data
    +    $digital_signature .= '';                  // Signature data (is empty)
    +
    +    $tmp_file_data = implode('', $file_data);  // File data
    +    $tmp_cd_data   = implode('', $cd_data).    // Central directory
    +                     $digital_signature;       // Digital signature
    +
    +    // End of central directory
    +    $eof_cd = '';
    +    $eof_cd .= "\x50\x4b\x05\x06";                // End of central dir signature
    +    $eof_cd .= pack("v", 0);                      // Number of this disk
    +    $eof_cd .= pack("v", 0);                      // Number of the disk with the start of the central directory
    +    $eof_cd .= pack("v", count($cd_data));        // Total number of entries in the central directory on this disk
    +    $eof_cd .= pack("v", count($cd_data));        // Total number of entries in the central directory
    +    $eof_cd .= pack("V", strlen($tmp_cd_data));   // Size of the central directory
    +    $eof_cd .= pack("V", strlen($tmp_file_data)); // Offset of start of central directory with respect to the starting disk number
    +    $eof_cd .= pack("v", 0);                      // .ZIP file comment length
    +    $eof_cd .= '';                                // .ZIP file comment (is empty)
    +
    +    // Content of the zip file
    +    $data = $tmp_file_data.
    +            // $extra_data_record.
    +            $tmp_cd_data.
    +            $eof_cd;
    +
    +    // Return content?
    +    if(!$filename)
    +      return $data;
    +      
    +    // Write to file
    +    return file_put_contents($filename, $data);
    +  }
    +  
    + /**
    +  *  Load a zip file
    +  *
    +  *  This function loads the files and dirs from a zip file from the harddrive.
    +  *
    +  *  @access                public
    +  *
    +  *  @param  string $file   The path to the zip file
    +  *  @param  bool   $reset  Reset the files and dirs before adding the zip file's content?
    +  *
    +  *  @return bool           Returns true if the file was loaded sucessfully
    +  */
    +  function load_file($file, $reset = true) {
    +    // Check whether the file exists
    +    if(!file_exists($file))
    +      return false;
    +
    +    // Load the files content
    +    $content = @file_get_contents($file);
    +
    +    // Return false if the file cannot be opened
    +    if(!$content)
    +      return false;
    +
    +    // Read the zip
    +    return $this->load_string($content, $reset);
    +  }
    +  
    + /**
    +  *  Load a zip string
    +  *
    +  *  This function loads the files and dirs from a string
    +  *
    +  *  @access                 public
    +  *
    +  *  @param  string $string  The string the zip is generated from
    +  *  @param  bool   $reset   Reset the files and dirs before adding the zip file's content?
    +  *
    +  *  @return bool            Returns true if the string was loaded sucessfully
    +  */
    +  function load_string($string, $reset = true) {
    +    // Reset the zip?
    +    if($reset) {
    +      $this->dirs  = array();
    +      $this->files = array();
    +    }
    +
    +    // Get the starting position of the end of central directory record
    +    $start = strpos($string, "\x50\x4b\x05\x06");
    +
    +    // Error
    +    if($start === false)
    +      die('Could not find the end of central directory record');
    +
    +    // Get the ecdr
    +    $eof_cd = substr($string, $start+4, 18);
    +
    +    // Unpack the ecdr infos
    +    $eof_cd = unpack('vdisc1/'.
    +                     'vdisc2/'.
    +                     'ventries1/'.
    +                     'ventries2/'.
    +                     'Vsize/'.
    +                     'Voffset/'.
    +                     'vcomment_lenght', $eof_cd);
    +
    +    // Do not allow multi disc zips
    +    if($eof_cd['disc1'] != 0)
    +      die('multi disk stuff is not yet implemented :/');
    +
    +    // Save the interesting values
    +    $cd_entries = $eof_cd['entries1'];
    +    $cd_size    = $eof_cd['size'];
    +    $cd_offset  = $eof_cd['offset'];
    +
    +    // Get the central directory record
    +    $cdr = substr($string, $cd_offset, $cd_size);
    +
    +    // Reset the position and the list of the entries
    +    $pos     = 0;
    +    $entries = array();
    +
    +    // Handle cdr
    +    while($pos < strlen($cdr)) {
    +      // Check header signature
    +      // Digital signature
    +      if(substr($cdr, $pos, 4) == "\x50\x4b\x05\x05") {
    +        // Get digital signature size
    +        $tmp_info = unpack('vsize', substr($cdr, $pos + 4, 2));
    +
    +        // Read out the digital signature
    +        $digital_sig = substr($header, $pos + 6, $tmp_info['size']);
    +
    +        break;
    +      }
    +
    +      // Get file header
    +      $header = substr($cdr, $pos, 46);
    +
    +      // Unpack the header information
    +      $header_info = @unpack('Vheader/'.
    +                             'vversion_made_by/'.
    +                             'vversion_needed/'.
    +                             'vgeneral_purpose/'.
    +                             'vcompression_method/'.
    +                             'vlast_mod_time/'.
    +                             'vlast_mod_date/'.
    +                             'Vcrc32/'.
    +                             'Vcompressed_size/'.
    +                             'Vuncompressed_size/'.
    +                             'vname_length/'.
    +                             'vextra_length/'.
    +                             'vcomment_length/'.
    +                             'vdisk_number/'.
    +                             'vinternal_attributes/'.
    +                             'Vexternal_attributes/'.
    +                             'Voffset',
    +                             $header);
    +
    +      // Valid header?
    +      if($header_info['header'] != 33639248)
    +        return false;
    +
    +      // New position
    +      $pos += 46;
    +
    +      // Read out the file name
    +      $header_info['name'] = substr($cdr, $pos, $header_info['name_length']);
    +
    +      // New position
    +      $pos += $header_info['name_length'];
    +
    +      // Read out the extra stuff
    +      $header_info['extra'] = substr($cdr, $pos, $header_info['extra_length']);
    +
    +      // New position
    +      $pos += $header_info['extra_length'];
    +
    +      // Read out the comment
    +      $header_info['comment'] = substr($cdr, $pos, $header_info['comment_length']);
    +
    +      // New position
    +      $pos += $header_info['comment_length'];
    +
    +      // Append this file/dir to the entry list
    +      $entries[] = $header_info;
    +    }
    +
    +    // Check whether all entries where read sucessfully
    +    if(count($entries) != $cd_entries)
    +      return false;
    +
    +    // Handle files/dirs
    +    foreach($entries as $entry) {
    +      // Is a dir?
    +      if($entry['external_attributes'] & 16) {
    +        $this->add_dir($entry['name']);
    +        continue;
    +      }
    +
    +      // Get local file header
    +      $header = substr($string, $entry['offset'], 30);
    +
    +      // Unpack the header information
    +      $header_info = @unpack('Vheader/'.
    +                             'vversion_needed/'.
    +                             'vgeneral_purpose/'.
    +                             'vcompression_method/'.
    +                             'vlast_mod_time/'.
    +                             'vlast_mod_date/'.
    +                             'Vcrc32/'.
    +                             'Vcompressed_size/'.
    +                             'Vuncompressed_size/'.
    +                             'vname_length/'.
    +                             'vextra_length',
    +                             $header);
    +
    +      // Valid header?
    +      if($header_info['header'] != 67324752)
    +        return false;
    +
    +      // Get content start position
    +      $start = $entry['offset'] + 30 + $header_info['name_length'] + $header_info['extra_length'];
    +
    +      // Get the compressed data
    +      $data = substr($string, $start, $header_info['compressed_size']);
    +
    +      // Detect compression type
    +      switch($header_info['compression_method']) {
    +        // No compression
    +        case 0:
    +          // Ne decompression needed
    +          $content = $data;
    +          break;
    +
    +        // Gzip
    +        case 8:
    +          if(!function_exists('gzinflate'))
    +            return false;
    +
    +          // Uncompress data
    +          $content = gzinflate($data);
    +          break;
    +
    +        // Bzip2
    +        case 12:
    +          if(!function_exists('bzdecompress'))
    +            return false;
    +
    +          // Decompress data
    +          $content = bzdecompress($data);
    +          break;
    +
    +        // Compression not supported -> error
    +        default:
    +          return false;
    +      }
    +
    +      // Try to add file
    +      if(!$this->add_file($entry['name'], $content))
    +        return false;
    +    }
    +
    +    return true;
    +  }
    +}
    +
    +function &byref() {
    +    $x = array();
    +    return $x;
    +}
    +?>
    diff --git a/tests/examplefiles/test.plot b/tests/examplefiles/test.plot
    new file mode 100644
    index 0000000..cef0f90
    --- /dev/null
    +++ b/tests/examplefiles/test.plot
    @@ -0,0 +1,333 @@
    +#
    +# $Id: prob2.dem,v 1.9 2006/06/14 03:24:09 sfeam Exp $
    +#
    +# Demo Statistical Approximations version 1.1
    +#
    +# Copyright (c) 1991, Jos van der Woude, jvdwoude@hut.nl
    +
    +# History:
    +#    -- --- 1991 Jos van der Woude:  1st version
    +#    06 Jun 2006 Dan Sebald:  Added plot methods for better visual effect.
    +
    +print ""
    +print ""
    +print ""
    +print ""
    +print ""
    +print ""
    +print "                        Statistical Approximations, version 1.1"
    +print ""
    +print "        Copyright (c) 1991, 1992, Jos van de Woude, jvdwoude@hut.nl"
    +print ""
    +print ""
    +print ""
    +print ""
    +print ""
    +print ""
    +print ""
    +print ""
    +print ""
    +print ""
    +print ""
    +print "     NOTE: contains 10 plots and consequently takes some time to run"
    +print "                      Press Ctrl-C to exit right now"
    +print ""
    +pause -1 "                      Press Return to start demo ..."
    +
    +load "stat.inc"
    +rnd(x) = floor(x+0.5)
    +r_xmin = -1
    +r_sigma = 4.0
    +
    +# Binomial PDF using normal approximation
    +n = 25; p = 0.15
    +mu = n * p
    +sigma = sqrt(n * p * (1.0 - p))
    +xmin = floor(mu - r_sigma * sigma)
    +xmin = xmin < r_xmin ? r_xmin : xmin
    +xmax = ceil(mu + r_sigma * sigma)
    +ymax = 1.1 * binom(floor((n+1)*p), n, p) #mode of binomial PDF used
    +set key box
    +unset zeroaxis
    +set xrange [xmin - 1 : xmax + 1]
    +set yrange [0 : ymax]
    +set xlabel "k, x ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%2.0f"
    +set format y "%3.2f"
    +set sample 200
    +set title "binomial PDF using normal approximation"
    +set arrow from mu, 0 to mu, normal(mu, mu, sigma) nohead
    +set arrow from mu, normal(mu + sigma, mu, sigma) \
    +          to mu + sigma, normal(mu + sigma, mu, sigma) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, normal(mu + sigma, mu, sigma)
    +plot binom(rnd(x), n, p) with histeps, normal(x, mu, sigma)
    +pause -1 "Hit return to continue"
    +unset arrow
    +unset label
    +
    +# Binomial PDF using poisson approximation
    +n = 50; p = 0.1
    +mu = n * p
    +sigma = sqrt(mu)
    +xmin = floor(mu - r_sigma * sigma)
    +xmin = xmin < r_xmin ? r_xmin : xmin
    +xmax = ceil(mu + r_sigma * sigma)
    +ymax = 1.1 * binom(floor((n+1)*p), n, p) #mode of binomial PDF used
    +set key box
    +unset zeroaxis
    +set xrange [xmin - 1 : xmax + 1]
    +set yrange [0 : ymax]
    +set xlabel "k ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%2.0f"
    +set format y "%3.2f"
    +set sample (xmax - xmin + 3)
    +set title "binomial PDF using poisson approximation"
    +set arrow from mu, 0 to mu, normal(mu, mu, sigma) nohead
    +set arrow from mu, normal(mu + sigma, mu, sigma) \
    +          to mu + sigma, normal(mu + sigma, mu, sigma) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, normal(mu + sigma, mu, sigma)
    +plot binom(x, n, p) with histeps, poisson(x, mu) with histeps
    +pause -1 "Hit return to continue"
    +unset arrow
    +unset label
    +
    +# Geometric PDF using gamma approximation
    +p = 0.3
    +mu = (1.0 - p) / p
    +sigma = sqrt(mu / p)
    +lambda = p
    +rho = 1.0 - p
    +xmin = floor(mu - r_sigma * sigma)
    +xmin = xmin < r_xmin ? r_xmin : xmin
    +xmax = ceil(mu + r_sigma * sigma)
    +ymax = 1.1 * p
    +set key box
    +unset zeroaxis
    +set xrange [xmin - 1 : xmax + 1]
    +set yrange [0 : ymax]
    +set xlabel "k, x ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%2.0f"
    +set format y "%3.2f"
    +set sample 200
    +set title "geometric PDF using gamma approximation"
    +set arrow from mu, 0 to mu, gmm(mu, rho, lambda) nohead
    +set arrow from mu, gmm(mu + sigma, rho, lambda) \
    +          to mu + sigma, gmm(mu + sigma, rho, lambda) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, gmm(mu + sigma, rho, lambda)
    +plot geometric(rnd(x),p) with histeps, gmm(x, rho, lambda)
    +pause -1 "Hit return to continue"
    +unset arrow
    +unset label
    +
    +# Geometric PDF using normal approximation
    +p = 0.3
    +mu = (1.0 - p) / p
    +sigma = sqrt(mu / p)
    +xmin = floor(mu - r_sigma * sigma)
    +xmin = xmin < r_xmin ? r_xmin : xmin
    +xmax = ceil(mu + r_sigma * sigma)
    +ymax = 1.1 * p
    +set key box
    +unset zeroaxis
    +set xrange [xmin - 1 : xmax + 1]
    +set yrange [0 : ymax]
    +set xlabel "k, x ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%2.0f"
    +set format y "%3.2f"
    +set sample 200
    +set title "geometric PDF using normal approximation"
    +set arrow from mu, 0 to mu, normal(mu, mu, sigma) nohead
    +set arrow from mu, normal(mu + sigma, mu, sigma) \
    +          to mu + sigma, normal(mu + sigma, mu, sigma) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, normal(mu + sigma, mu, sigma)
    +plot geometric(rnd(x),p) with histeps, normal(x, mu, sigma)
    +pause -1 "Hit return to continue"
    +unset arrow
    +unset label
    +
    +# Hypergeometric PDF using binomial approximation
    +nn = 75; mm = 25; n = 10
    +p = real(mm) / nn
    +mu = n * p
    +sigma = sqrt(real(nn - n) / (nn - 1.0) * n * p * (1.0 - p))
    +xmin = floor(mu - r_sigma * sigma)
    +xmin = xmin < r_xmin ? r_xmin : xmin
    +xmax = ceil(mu + r_sigma * sigma)
    +ymax = 1.1 * hypgeo(floor(mu), nn, mm, n) #mode of binom PDF used
    +set key box
    +unset zeroaxis
    +set xrange [xmin - 1 : xmax + 1]
    +set yrange [0 : ymax]
    +set xlabel "k ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%2.0f"
    +set format y "%3.2f"
    +set sample (xmax - xmin + 3)
    +set title "hypergeometric PDF using binomial approximation"
    +set arrow from mu, 0 to mu, binom(floor(mu), n, p) nohead
    +set arrow from mu, binom(floor(mu + sigma), n, p) \
    +          to mu + sigma, binom(floor(mu + sigma), n, p) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, binom(floor(mu + sigma), n, p)
    +plot hypgeo(x, nn, mm, n) with histeps, binom(x, n, p) with histeps
    +pause -1 "Hit return to continue"
    +unset arrow
    +unset label
    +
    +# Hypergeometric PDF using normal approximation
    +nn = 75; mm = 25; n = 10
    +p = real(mm) / nn
    +mu = n * p
    +sigma = sqrt(real(nn - n) / (nn - 1.0) * n * p * (1.0 - p))
    +xmin = floor(mu - r_sigma * sigma)
    +xmin = xmin < r_xmin ? r_xmin : xmin
    +xmax = ceil(mu + r_sigma * sigma)
    +ymax = 1.1 * hypgeo(floor(mu), nn, mm, n) #mode of binom PDF used
    +set key box
    +unset zeroaxis
    +set xrange [xmin - 1 : xmax + 1]
    +set yrange [0 : ymax]
    +set xlabel "k, x ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%2.0f"
    +set format y "%3.2f"
    +set sample 200
    +set title "hypergeometric PDF using normal approximation"
    +set arrow from mu, 0 to mu, normal(mu, mu, sigma) nohead
    +set arrow from mu, normal(mu + sigma, mu, sigma) \
    +          to mu + sigma, normal(mu + sigma, mu, sigma) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, normal(mu + sigma, mu, sigma)
    +plot hypgeo(rnd(x), nn, mm, n) with histeps, normal(x, mu, sigma)
    +pause -1 "Hit return to continue"
    +unset arrow
    +unset label
    +
    +# Negative binomial PDF using gamma approximation
    +r = 8; p = 0.6
    +mu = r * (1.0 - p) / p
    +sigma = sqrt(mu / p)
    +lambda = p
    +rho = r * (1.0 - p)
    +xmin = floor(mu - r_sigma * sigma)
    +xmin = xmin < r_xmin ? r_xmin : xmin
    +xmax = ceil(mu + r_sigma * sigma)
    +ymax = 1.1 * gmm((rho - 1) / lambda, rho, lambda) #mode of gamma PDF used
    +set key box
    +unset zeroaxis
    +set xrange [xmin - 1 : xmax + 1]
    +set yrange [0 : ymax]
    +set xlabel "k, x ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%2.0f"
    +set format y "%3.2f"
    +set sample 200
    +set title "negative binomial PDF using gamma approximation"
    +set arrow from mu, 0 to mu, gmm(mu, rho, lambda) nohead
    +set arrow from mu, gmm(mu + sigma, rho, lambda) \
    +          to mu + sigma, gmm(mu + sigma, rho, lambda) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, gmm(mu + sigma, rho, lambda)
    +plot negbin(rnd(x), r, p) with histeps, gmm(x, rho, lambda)
    +pause -1 "Hit return to continue"
    +unset arrow
    +unset label
    +
    +# Negative binomial PDF using normal approximation
    +r = 8; p = 0.4
    +mu = r * (1.0 - p) / p
    +sigma = sqrt(mu / p)
    +xmin = floor(mu - r_sigma * sigma)
    +xmin = xmin < r_xmin ? r_xmin : xmin
    +xmax = ceil(mu + r_sigma * sigma)
    +ymax = 1.1 * negbin(floor((r-1)*(1-p)/p), r, p) #mode of gamma PDF used
    +set key box
    +unset zeroaxis
    +set xrange [xmin - 1 : xmax + 1]
    +set yrange [0 : ymax]
    +set xlabel "k, x ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%2.0f"
    +set format y "%3.2f"
    +set sample 200
    +set title "negative binomial PDF using normal approximation"
    +set arrow from mu, 0 to mu, normal(mu, mu, sigma) nohead
    +set arrow from mu, normal(mu + sigma, mu, sigma) \
    +          to mu + sigma, normal(mu + sigma, mu, sigma) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, normal(mu + sigma, mu, sigma)
    +plot negbin(rnd(x), r, p) with histeps, normal(x, mu, sigma)
    +pause -1 "Hit return to continue"
    +unset arrow
    +unset label
    +
    +# Normal PDF using logistic approximation
    +mu = 1.0; sigma = 1.5
    +a = mu
    +lambda = pi / (sqrt(3.0) * sigma)
    +xmin = mu - r_sigma * sigma
    +xmax = mu + r_sigma * sigma
    +ymax = 1.1 * logistic(mu, a, lambda) #mode of logistic PDF used
    +set key box
    +unset zeroaxis
    +set xrange [xmin: xmax]
    +set yrange [0 : ymax]
    +set xlabel "x ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%.1f"
    +set format y "%.2f"
    +set sample 200
    +set title "normal PDF using logistic approximation"
    +set arrow from mu,0 to mu, normal(mu, mu, sigma) nohead
    +set arrow from mu, normal(mu + sigma, mu, sigma) \
    +          to mu + sigma, normal(mu + sigma, mu, sigma) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, normal(mu + sigma, mu, sigma)
    +plot logistic(x, a, lambda), normal(x, mu, sigma)
    +pause -1 "Hit return to continue"
    +unset arrow
    +unset label
    +
    +# Poisson PDF using normal approximation
    +mu = 5.0
    +sigma = sqrt(mu)
    +xmin = floor(mu - r_sigma * sigma)
    +xmin = xmin < r_xmin ? r_xmin : xmin
    +xmax = ceil(mu + r_sigma * sigma)
    +ymax = 1.1 * poisson(mu, mu) #mode of poisson PDF used
    +set key box
    +unset zeroaxis
    +set xrange [xmin - 1 : xmax + 1]
    +set yrange [0 : ymax]
    +set xlabel "k, x ->"
    +set ylabel "probability density ->"
    +set ytics 0, ymax / 10.0, ymax
    +set format x "%2.0f"
    +set format y "%3.2f"
    +set sample 200
    +set title "poisson PDF using normal approximation"
    +set arrow from mu, 0 to mu, normal(mu, mu, sigma) nohead
    +set arrow from mu, normal(mu + sigma, mu, sigma) \
    +          to mu + sigma, normal(mu + sigma, mu, sigma) nohead
    +set label "mu" at mu + 0.5, ymax / 10
    +set label "sigma" at mu + 0.5 + sigma, normal(mu + sigma, mu, sigma)
    +plot poisson(rnd(x), mu) with histeps, normal(x, mu, sigma)
    +pause -1 "Hit return to continue"
    +reset
    diff --git a/tests/examplefiles/test.r3 b/tests/examplefiles/test.r3
    new file mode 100644
    index 0000000..cad12a8
    --- /dev/null
    +++ b/tests/examplefiles/test.r3
    @@ -0,0 +1,94 @@
    +;## String tests ##
    +print "Hello ^"World" ;<- with escaped char
    +multiline-string: {
    +    bla bla "bla" {bla}
    +}
    +char-a: #"a"
    +escaped-a: #"^(61)"
    +new-line: #"^/"
    +
    +;## Binaries ##
    +print decompress 64#{eJzLSM3JyQcABiwCFQUAAAA=}
    +;2#{0000 00000} ;<- this one is invalid!
    +2#{}
    +#{FF00}
    +
    +;##Date + time ##
    +1-Feb-2009
    +1-Feb-2009/2:24:46+1:0
    +1:0 1:1:1 -0:1.1
    +
    +;## Tuple ##
    +red: 255.0.0
    +red-with-alpha: 255.0.0.100
    +
    +;## url!, file! and email! ##
    +aaa@bbb.cz
    +http://
    +dns://
    +tcp://127.0.0.1
    +%/c/rebol/
    +%"c:\Program Files\"
    +%/c/Program%20Files/
    +to-rebol-file "c:\Program Files\"
    +suffix? %bla.swf
    +
    +;## Money ##
    +$1
    +-$1.2
    +USA$100
    +
    +;## Tag! ##
    +
    +
    +
    +;## Pair! ##
    +10x200
    +
    +;## Issue! ##
    +type? #ff0000 ;== issue!
    +
    +;## some numbers ##
    +to integer! (1 + (x / 4.5) * 1E-4)
    +
    +;## some spec comments
    +comment now
    +comment 10
    +comment {
    +    bla
    +    bla
    +}
    +comment [
    +    quit
    +]
    +
    +;## other tests ##
    +---: 1
    +x/(1 + n)/y
    +b/:1
    +
    +;## and...
    +REBOL [
    +    purpose: {
    +        reads css file and creates html from it
    +        so one can see how the styles looks like
    +    } 
    +]
    +style: %default
    +out: rejoin [{
    +
    +
    +  Pygments style: } style {.css
    +  
    +
    +
    +
    +}]
    +css: read/lines join style %.css
    +foreach line css [
    +    parse line [".syntax ." copy c to " " thru "/*" copy t to "*/" to end (
    +        append out rejoin ["" t "^/"])
    +    ]
    +]
    +write join style %.html join out "
    " +halt diff --git a/tests/examplefiles/test.rb b/tests/examplefiles/test.rb new file mode 100644 index 0000000..1f609e3 --- /dev/null +++ b/tests/examplefiles/test.rb @@ -0,0 +1,174 @@ +a.each{|el|anz[el]=anz[el]?anz[el]+1:1} +while x<10000 +#a bis f dienen dazu die Nachbarschaft festzulegen. Man stelle sich die #Zahl von 1 bis 64 im Binärcode vor 1 bedeutet an 0 aus + b=(p[x]%32)/16<1 ? 0 : 1 + + (x-102>=0? n[x-102].to_i : 0)*a+(x-101>=0?n[x-101].to_i : 0)*e+n[x-100].to_i+(x-99>=0? n[x-99].to_i : 0)*f+(x-98>=0? n[x-98].to_i : 0)*a+ + n[x+199].to_i*b+n[x+200].to_i*d+n[x+201].to_i*b + +#und die Ausgabe folgt +g=%w{} +x=0 + +while x<100 + puts"#{g[x]}" + x+=1 +end + +puts"" +sleep(10) + +1E1E1 +puts 30.send(:/, 5) # prints 6 + +# fun with class attributes +class Foo + def self.blub x + if not x.nil? + self.new + end + end + def another_way_to_get_class + self.class + end +end + +# ruby 1.9 "call operator" +a = Proc.new { 42 } +a.() + +"instance variables can be #@included, #@@class_variables\n and #$globals as well." +`instance variables can be #@included, #@@class_variables\n and #$globals as well.` +'instance variables can be #@included, #@@class_variables\n and #$globals as well.' +/instance variables can be #@included, #@@class_variables\n and #$globals as well./mousenix +:"instance variables can be #@included, #@@class_variables\n and #$globals as well." +:'instance variables can be #@included, #@@class_variables\n and #$globals as well.' +%'instance variables can be #@included, #@@class_variables\n and #$globals as well.' +%q'instance variables can be #@included, #@@class_variables\n and #$globals as well.' +%Q'instance variables can be #@included, #@@class_variables\n and #$globals as well.' +%w'instance variables can be #@included, #@@class_variables\n and #$globals as well.' +%W'instance variables can be #@included, #@@class_variables\n and #$globals as well.' +%s'instance variables can be #@included, #@@class_variables\n and #$globals as well.' +%r'instance variables can be #@included, #@@class_variables\n and #$globals as well.' +%x'instance variables can be #@included, #@@class_variables\n and #$globals as well.' + +#%W[ but #@0illegal_values look strange.] + +%s#ruby allows strange#{constructs} +%s#ruby allows strange#$constructs +%s#ruby allows strange#@@constructs + +################################################################## +# HEREDOCS +foo(<<-A, <<-B) +this is the text of a +A +and this is the text of b +B + +a = <<"EOF" +This is a multiline #$here document +terminated by EOF on a line by itself +EOF + +a = <<'EOF' +This is a multiline #$here document +terminated by EOF on a line by itself +EOF + +b=(p[x] %32)/16<1 ? 0 : 1 + +<<"" +#{test} +#@bla +#die suppe!!! +\xfffff + + +super <<-EOE % [ + foo +EOE + +< [1, 2, 3, 4, 5, 6] +p [1,2,3].`(:concat, [4,5,6]) # => [1, 2, 3, 4, 5, 6] +p "Hurra! ".`(:*, 3) # => "Hurra! Hurra! Hurra! " +p "Hurra! ".`('*', 3) # => "Hurra! Hurra! Hurra! " +# Leider geht nicht die Wunschform +# [1,2,3] `concat` [4,5,6] + +class Object + @@infixops = [] + alias :xeq :` + def addinfix(operator) + @@infixops << operator + end + def `(expression) + @@infixops.each{|op|break if expression.match(/^(.*?) (#{op}) (.*)$/)} + raise "unknown infix operator in expression: #{expression}" if $2 == nil + eval($1).method($2.to_sym).call(eval($3)) + end +end +addinfix("concat") +p `[1,2,3] concat [4,5,6]` # => [1, 2, 3, 4, 5, 6] + + +# HEREDOC FUN!!!!!!!1111 +foo(< + <% rows.each do |row| %> + +
    <%= item.title %> + <%= item.description %> + + <% end %> + + + +

    Pages

    + + + + + + + + + + +<% if @homepage -%> +<%= render_node @homepage -%> +<% else -%> + + + +<% end -%> + +
    PageStatusModify
    No Pages
    + +
    +

    +<% unless @homepage -%> + <%= link_to image_tag('new-homepage', :alt => 'New Homepage'), homepage_new_url %> +<% end -%> + <%= image_submit_tag 'clear-page-cache' %> +

    +
    diff --git a/tests/examplefiles/test.tcsh b/tests/examplefiles/test.tcsh new file mode 100644 index 0000000..e215ed0 --- /dev/null +++ b/tests/examplefiles/test.tcsh @@ -0,0 +1,830 @@ +# +# $Id: complete.tcsh,v 1.2 1998/05/11 10:40:54 luisgh Exp $ +# example file using the new completion code +# + +# Debian GNU/Linux +# file: /usr/share/doc/examples/tcsh/complete.tcsh +# +# This file may be read from user's .cshrc file by decompressing it into +# the home directory as ~/.complete and then adding the line +# "source ${HOME}/.complete" and maybe defining some of +# the shell variables described below. +# +# Debian enhancements by Vadim Vygonets : +# +# Added two Debian-specific completions: dpkg and dpkg-deb (who wrote +# them?). To turn it off, define no_debian_complete before loading +# this file. +# +# Added some new completions. To turn them off, define +# no_new_complete before loading this file. +# +# Changed completions of several commands. The ones are evaluated if +# the following shell variables are defined: +# +# traditional_cp_mv_complete +# for traditional completion of cp and mv commands +# traditional_zcat_complete +# for traditional completion of zcat command +# traditional_nm_complete +# for traditional completion of nm command +# traditilnal_tex_complete +# for traditional completion of tex command +# traditional_find_complete +# for traditional completion of find command +# traditional_configure_complete +# for traditional completion of ./configure command +# foolproof_rm_complete or traditional_rm_complete +# for traditional completion of rm command +# traditional_complete +# all of the above + +if ($?traditional_complete) then + set traditional_cp_mv_complete + set traditional_zcat_complete + set traditional_nm_complete + set traditilnal_tex_complete + set traditional_find_complete + set traditional_configure_complete + set foolproof_rm_complete +endif + +if ($?traditional_rm_complete) then + set foolproof_rm_complete +endif + +onintr - +if (! $?prompt) goto end + +if ($?tcsh) then + if ($tcsh != 1) then + set rev=$tcsh:r + set rel=$rev:e + set pat=$tcsh:e + set rev=$rev:r + endif + if ($rev > 5 && $rel > 1) then + set complete=1 + endif + unset rev rel pat +endif + +if ($?complete) then + set noglob + set hosts + foreach f ($HOME/.hosts /usr/local/etc/csh.hosts $HOME/.rhosts /etc/hosts.equiv) + if ( -r $f ) then + set hosts=($hosts `cut -d " " -f 1 $f | grep -v +`) + endif + end + if ( -r $HOME/.netrc ) then + set f=`awk '/machine/ { print $2 }' < $HOME/.netrc` >& /dev/null + set hosts=($hosts $f) + endif + unset f + if ( ! $?hosts ) then + set hosts=(hyperion.ee.cornell.edu phaeton.ee.cornell.edu \ + guillemin.ee.cornell.edu vangogh.cs.berkeley.edu \ + ftp.uu.net prep.ai.mit.edu export.lcs.mit.edu \ + labrea.stanford.edu sumex-aim.stanford.edu \ + tut.cis.ohio-state.edu) + endif + + complete ywho n/*/\$hosts/ # argument from list in $hosts + complete rsh p/1/\$hosts/ c/-/"(l n)"/ n/-l/u/ N/-l/c/ n/-/c/ p/2/c/ p/*/f/ + complete xrsh p/1/\$hosts/ c/-/"(l 8 e)"/ n/-l/u/ N/-l/c/ n/-/c/ p/2/c/ p/*/f/ + complete rlogin p/1/\$hosts/ c/-/"(l 8 e)"/ n/-l/u/ + complete telnet p/1/\$hosts/ p/2/x:''/ n/*/n/ + + complete cd p/1/d/ # Directories only + complete chdir p/1/d/ + complete pushd p/1/d/ + complete popd p/1/d/ + complete pu p/1/d/ + complete po p/1/d/ + complete complete p/1/X/ # Completions only + complete uncomplete n/*/X/ + complete exec p/1/c/ # Commands only + complete trace p/1/c/ + complete strace p/1/c/ + complete which n/*/c/ + complete where n/*/c/ + complete skill p/1/c/ + complete dde p/1/c/ + complete adb c/-I/d/ n/-/c/ N/-/"(core)"/ p/1/c/ p/2/"(core)"/ + complete sdb p/1/c/ + complete dbx c/-I/d/ n/-/c/ N/-/"(core)"/ p/1/c/ p/2/"(core)"/ + complete xdb p/1/c/ + complete gdb n/-d/d/ n/*/c/ + complete ups p/1/c/ + complete set 'c/*=/f/' 'p/1/s/=' 'n/=/f/' + complete unset n/*/s/ + complete alias p/1/a/ # only aliases are valid + complete unalias n/*/a/ + complete xdvi n/*/f:*.dvi/ # Only files that match *.dvi + complete dvips n/*/f:*.dvi/ +if ($?traditilnal_tex_complete) then + complete tex n/*/f:*.tex/ # Only files that match *.tex +else + complete tex n/*/f:*.{tex,texi}/ # Files that match *.tex and *.texi +endif + complete latex n/*/f:*.{tex,ltx}/ + complete su c/--/"(login fast preserve-environment command shell \ + help version)"/ c/-/"(f l m p c s -)"/ \ + n/{-c,--command}/c/ \ + n@{-s,--shell}@'`cat /etc/shells`'@ n/*/u/ + complete cc c/-[IL]/d/ \ + c@-l@'`\ls -1 /usr/lib/lib*.a | sed s%^.\*/lib%%\;s%\\.a\$%%`'@ \ + c/-/"(o l c g L I D U)"/ n/*/f:*.[coasi]/ + complete acc c/-[IL]/d/ \ + c@-l@'`\ls -1 /usr/lang/SC1.0/lib*.a | sed s%^.\*/lib%%\;s%\\.a\$%%`'@ \ + c/-/"(o l c g L I D U)"/ n/*/f:*.[coasi]/ + complete gcc c/-[IL]/d/ \ + c/-f/"(caller-saves cse-follow-jumps delayed-branch \ + elide-constructors expensive-optimizations \ + float-store force-addr force-mem inline \ + inline-functions keep-inline-functions \ + memoize-lookups no-default-inline \ + no-defer-pop no-function-cse omit-frame-pointer \ + rerun-cse-after-loop schedule-insns \ + schedule-insns2 strength-reduce \ + thread-jumps unroll-all-loops \ + unroll-loops syntax-only all-virtual \ + cond-mismatch dollars-in-identifiers \ + enum-int-equiv no-asm no-builtin \ + no-strict-prototype signed-bitfields \ + signed-char this-is-variable unsigned-bitfields \ + unsigned-char writable-strings call-saved-reg \ + call-used-reg fixed-reg no-common \ + no-gnu-binutils nonnull-objects \ + pcc-struct-return pic PIC shared-data \ + short-enums short-double volatile)"/ \ + c/-W/"(all aggregate-return cast-align cast-qual \ + comment conversion enum-clash error format \ + id-clash-len implicit missing-prototypes \ + no-parentheses pointer-arith return-type shadow \ + strict-prototypes switch uninitialized unused \ + write-strings)"/ \ + c/-m/"(68000 68020 68881 bitfield fpa nobitfield rtd \ + short c68000 c68020 soft-float g gnu unix fpu \ + no-epilogue)"/ \ + c/-d/"(D M N)"/ \ + c/-/"(f W vspec v vpath ansi traditional \ + traditional-cpp trigraphs pedantic x o l c g L \ + I D U O O2 C E H B b V M MD MM i dynamic \ + nodtdlib static nostdinc undef)"/ \ + c/-l/f:*.a/ \ + n/*/f:*.{c,C,cc,o,a,s,i}/ + complete g++ n/*/f:*.{C,cc,o,s,i}/ + complete CC n/*/f:*.{C,cc,o,s,i}/ +if ($?foolproof_rm_complete) then + complete rm c/--/"(directory force interactive verbose \ + recursive help version)"/ c/-/"(d f i v r R -)"/ \ + n/*/f:^*.{c,cc,C,h,in}/ # Protect precious files +else + complete rm c/--/"(directory force interactive verbose \ + recursive help version)"/ c/-/"(d f i v r R -)"/ +endif + complete vi n/*/f:^*.[oa]/ + complete bindkey N/-a/b/ N/-c/c/ n/-[ascr]/'x:'/ \ + n/-[svedlr]/n/ c/-[vedl]/n/ c/-/"(a s k c v e d l r)"/\ + n/-k/"(left right up down)"/ p/2-/b/ \ + p/1/'x:'/ + +if ($?traditional_find_complete) then + complete find n/-fstype/"(nfs 4.2)"/ n/-name/f/ \ + n/-type/"(c b d f p l s)"/ n/-user/u/ n/-group/g/ \ + n/-exec/c/ n/-ok/c/ n/-cpio/f/ n/-ncpio/f/ n/-newer/f/ \ + c/-/"(fstype name perm prune type user nouser \ + group nogroup size inum atime mtime ctime exec \ + ok print ls cpio ncpio newer xdev depth \ + daystart follow maxdepth mindepth noleaf version \ + anewer cnewer amin cmin mmin true false uid gid \ + ilname iname ipath iregex links lname empty path \ + regex used xtype fprint fprint0 fprintf \ + print0 printf not a and o or)"/ \ + n/*/d/ +else + complete find n/-fstype/"(ufs nfs tmp mfs minix ext2 msdos umsdos vfat proc iso9660 4.2 4.3 local)"/ \ + n/-name/f/ \ + n/-type/"(c b d f p l s)"/ n/-user/u/ n/-group/g/ \ + n/-exec/c/ n/-ok/c/ n/-cpio/f/ n/-ncpio/f/ n/-newer/f/ \ + c/-/"(fstype name perm prune type user nouser \ + group nogroup size inum atime mtime ctime exec \ + ok print ls cpio ncpio newer xdev depth \ + daystart follow maxdepth mindepth noleaf version \ + anewer cnewer amin cmin mmin true false uid gid \ + ilname iname ipath iregex links lname empty path \ + regex used xtype fprint fprint0 fprintf \ + print0 printf not a and o or)"/ \ + n/*/d/ +endif + complete -%* c/%/j/ # fill in the jobs builtin + complete {fg,bg,stop} c/%/j/ p/1/"(%)"// + + complete limit c/-/"(h)"/ n/*/l/ + complete unlimit c/-/"(h)"/ n/*/l/ + + complete -co* p/0/"(compress)"/ # make compress completion + # not ambiguous +if ($?traditional_zcat_complete) then + complete zcat n/*/f:*.Z/ +else + complete zcat c/--/"(force help license quiet version)"/ \ + c/-/"(f h L q V -)"/ n/*/f:*.{gz,Z,z,zip}/ +endif +if ($?traditional_nm_complete) then + complete nm n/*/f:^*.{h,C,c,cc}/ +else +complete nm 'c/--radix=/x:/' \ + 'c/--target=/x:/' \ + 'c/--format=/(bsd sysv posix)/n/' \ + 'c/--/(debugsyms extern-only demangle dynamic print-armap \ + print-file-name numeric-sort no-sort reverse-sort \ + size-sort undefined-only portability target= radix= \ + format= defined-only\ line-numbers no-demangle version \ + help)//' \ + 'n/*/f:^*.{h,c,cc,s,S}/' +endif + + complete finger c/*@/\$hosts/ n/*/u/@ + complete ping p/1/\$hosts/ + complete traceroute p/1/\$hosts/ + + complete {talk,ntalk,phone} p/1/'`users | tr " " "\012" | uniq`'/ \ + n/*/\`who\ \|\ grep\ \$:1\ \|\ awk\ \'\{\ print\ \$2\ \}\'\`/ + + complete ftp c/-/"(d i g n v)"/ n/-/\$hosts/ p/1/\$hosts/ n/*/n/ + + # this one is simple... + #complete rcp c/*:/f/ C@[./\$~]*@f@ n/*/\$hosts/: + # From Michael Schroeder + # This one will rsh to the file to fetch the list of files! + complete rcp 'c%*@*:%`set q=$:-0;set q="$q:s/@/ /";set q="$q:s/:/ /";set q=($q " ");rsh $q[2] -l $q[1] ls -dp $q[3]\*`%' 'c%*:%`set q=$:-0;set q="$q:s/:/ /";set q=($q " ");rsh $q[1] ls -dp $q[2]\*`%' 'c%*@%$hosts%:' 'C@[./$~]*@f@' 'n/*/$hosts/:' + + complete dd c/--/"(help version)"/ c/[io]f=/f/ \ + c/conv=*,/"(ascii ebcdic ibm block unblock \ + lcase ucase swab noerror sync)"/,\ + c/conv=/"(ascii ebcdic ibm block unblock \ + lcase ucase swab noerror sync)"/,\ + c/*=/x:''/ \ + n/*/"(if of conv ibs obs bs cbs files skip file seek count)"/= + + complete nslookup p/1/x:''/ p/2/\$hosts/ + + complete ar c/[dmpqrtx]/"(c l o u v a b i)"/ p/1/"(d m p q r t x)"// \ + p/2/f:*.a/ p/*/f:*.o/ + + complete {refile,sprev,snext,scan,pick,rmm,inc,folder,show} \ + c@+@F:$HOME/Mail/@ + + # these and interrupt handling from Jaap Vermeulen + complete {rexec,rxexec,rxterm,rmterm} \ + 'p/1/$hosts/' 'c/-/(l L E)/' 'n/-l/u/' 'n/-L/f/' \ + 'n/-E/e/' 'n/*/c/' + complete kill 'c/-/S/' 'c/%/j/' \ + 'n/*/`ps -u $LOGNAME | awk '"'"'{print $1}'"'"'`/' + + # these from Marc Horowitz + complete attach 'n/-mountpoint/d/' 'n/-m/d/' 'n/-type/(afs nfs rvd ufs)/' \ + 'n/-t/(afs nfs rvd ufs)/' 'n/-user/u/' 'n/-U/u/' \ + 'c/-/(verbose quiet force printpath lookup debug map \ + nomap remap zephyr nozephyr readonly write \ + mountpoint noexplicit explicit type mountoptions \ + nosetuid setuid override skipfsck lock user host)/' \ + 'n/-e/f/' 'n/*/()/' + complete hesinfo 'p/1/u/' \ + 'p/2/(passwd group uid grplist pcap pobox cluster \ + filsys sloc service)/' + + # these from E. Jay Berkenbilt +if ($?traditional_configure_complete) then + complete ./configure 'c/--*=/f/' 'c/--{cache-file,prefix,srcdir}/(=)//' \ + 'c/--/(cache-file verbose prefix srcdir)//' +else +complete ./configure \ + 'c@--{prefix,exec-prefix,bindir,sbindir,libexecdir,datadir,sysconfdir,sharedstatedir,localstatedir,infodir,mandir,srcdir,x-includes,x-libraries}=*@x:'@ \ + 'c/--cachefile=*/x:/' \ + 'c/--{enable,disable,with}-*/x://' \ + 'c/--*=/x:/' \ + 'c/--/(prefix= exec-prefix= bindir= sbindir= \ + libexecdir= datadir= sysconfdir= \ + sharedstatedir= localstatedir= infodir= \ + mandir= srcdir= x-includes= x-libraries= \ + enable- disable- with- )//' \ + 'c/--(help no-create quiet silent version \ + verbose)/' +endif + complete gs 'c/-sDEVICE=/(x11 cdjmono cdj550 epson eps9high epsonc \ + dfaxhigh dfaxlow laserjet ljet4 sparc pbm \ + pbmraw pgm pgmraw ppm ppmraw bit)/' \ + 'c/-sOutputFile=/f/' 'c/-s/(DEVICE OutputFile)/=' \ + 'c/-d/(NODISPLAY NOPLATFONTS NOPAUSE)/' 'n/*/f/' + complete perl 'n/-S/c/' + complete printenv 'n/*/e/' + complete sccs p/1/"(admin cdc check clean comb deledit delget \ + delta diffs edit enter fix get help info \ + print prs prt rmdel sccsdiff tell unedit \ + unget val what)"/ + complete setenv 'p/1/e/' 'c/*:/f/' + + # these and method of setting hosts from Kimmo Suominen + if ( -f $HOME/.mh_profile && -x "`which folders`" ) then + + if ( ! $?FOLDERS ) setenv FOLDERS "`folders -fast -recurse`" + if ( ! $?MHA ) setenv MHA "`ali | sed -e '/^ /d' -e 's/:.*//'`" + + set folders = ( $FOLDERS ) + set mha = ( $MHA ) + + complete ali \ + 'c/-/(alias nolist list nonormalize normalize nouser user help)/' \ + 'n,-alias,f,' + + complete anno \ + 'c/-/(component noinplace inplace nodate date text help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete burst \ + 'c/-/(noinplace inplace noquiet quiet noverbose verbose help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete comp \ + 'c/-/(draftfolder draftmessage nodraftfolder editor noedit file form nouse use whatnowproc nowhatnowproc help)/' \ + 'c,+,$folders,' \ + 'n,-whatnowproc,c,' \ + 'n,-file,f,'\ + 'n,-form,f,'\ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete dist \ + 'c/-/(noannotate annotate draftfolder draftmessage nodraftfolder editor noedit form noinplace inplace whatnowproc nowhatnowproc help)/' \ + 'c,+,$folders,' \ + 'n,-whatnowproc,c,' \ + 'n,-form,f,'\ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete folder \ + 'c/-/(all nofast fast noheader header nopack pack noverbose verbose norecurse recurse nototal total noprint print nolist list push pop help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete folders \ + 'c/-/(all nofast fast noheader header nopack pack noverbose verbose norecurse recurse nototal total noprint print nolist list push pop help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete forw \ + 'c/-/(noannotate annotate draftfolder draftmessage nodraftfolder editor noedit filter form noformat format noinplace inplace digest issue volume whatnowproc nowhatnowproc help)/' \ + 'c,+,$folders,' \ + 'n,-whatnowproc,c,' \ + 'n,-filter,f,'\ + 'n,-form,f,'\ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete inc \ + 'c/-/(audit file noaudit nochangecur changecur file form format nosilent silent notruncate truncate width help)/' \ + 'c,+,$folders,' \ + 'n,-audit,f,'\ + 'n,-form,f,' + + complete mark \ + 'c/-/(add delete list sequence nopublic public nozero zero help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete mhmail \ + 'c/-/(body cc from subject help)/' \ + 'n,-cc,$mha,' \ + 'n,-from,$mha,' \ + 'n/*/$mha/' + + complete mhpath \ + 'c/-/(help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete msgchk \ + 'c/-/(nodate date nonotify notify help)/' + + complete msh \ + 'c/-/(prompt noscan scan notopcur topcur help)/' + + complete next \ + 'c/-/(draft form moreproc nomoreproc length width showproc noshowproc header noheader help)/' \ + 'c,+,$folders,' \ + 'n,-moreproc,c,' \ + 'n,-showproc,c,' \ + 'n,-form,f,' + + complete packf \ + 'c/-/(file help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete pick \ + 'c/-/(and or not lbrace rbrace cc date from search subject to othercomponent after before datefield sequence nopublic public nozero zero nolist list help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete prev \ + 'c/-/(draft form moreproc nomoreproc length width showproc noshowproc header noheader help)/' \ + 'c,+,$folders,' \ + 'n,-moreproc,c,' \ + 'n,-showproc,c,' \ + 'n,-form,f,' + + complete prompter \ + 'c/-/(erase kill noprepend prepend norapid rapid nodoteof doteof help)/' + + complete refile \ + 'c/-/(draft nolink link nopreserve preserve src file help)/' \ + 'c,+,$folders,' \ + 'n,-file,f,'\ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete rmf \ + 'c/-/(nointeractive interactive help)/' \ + 'c,+,$folders,' + + complete rmm \ + 'c/-/(help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete scan \ + 'c/-/(noclear clear form format noheader header width noreverse reverse file help)/' \ + 'c,+,$folders,' \ + 'n,-form,f,'\ + 'n,-file,f,'\ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete send \ + 'c/-/(alias draft draftfolder draftmessage nodraftfolder filter nofilter noformat format noforward forward nomsgid msgid nopush push noverbose verbose nowatch watch width help)/' \ + 'n,-alias,f,'\ + 'n,-filter,f,' + + complete show \ + 'c/-/(draft form moreproc nomoreproc length width showproc noshowproc header noheader help)/' \ + 'c,+,$folders,' \ + 'n,-moreproc,c,' \ + 'n,-showproc,c,' \ + 'n,-form,f,'\ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete sortm \ + 'c/-/(datefield textfield notextfield limit nolimit noverbose verbose help)/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete vmh \ + 'c/-/(prompt vmhproc novmhproc help)/' \ + 'n,-vmhproc,c,' + + complete whatnow \ + 'c/-/(draftfolder draftmessage nodraftfolder editor noedit prompt help)/' + + complete whom \ + 'c/-/(alias nocheck check draft draftfolder draftmessage nodraftfolder help)/' \ + 'n,-alias,f,' + + complete plum \ + 'c/-/()/' \ + 'c,+,$folders,' \ + 'n,*,`(mark | sed "s/:.*//";echo next cur prev first last)|tr " " "\12" | sort -u`,' + + complete mail \ + 'c/-/()/' \ + 'n/*/$mha/' + + endif + + # these from Tom Warzeka + # you may need to set the following variables for your host + set _elispdir = /usr/lib/emacs/19.34/lisp # GNU Emacs lisp directory + set _maildir = /var/spool/mail # Post Office: /var/spool/mail or /usr/mail + set _ypdir = /var/yp # directory where NIS (YP) maps are kept + set _domain = "`dnsdomainname`" + + # this one works but is slow and doesn't descend into subdirectories + # complete cd C@[./\$~]*@d@ \ + # p@1@'`\ls -1F . $cdpath | grep /\$ | sort -u`'@ n@*@n@ + + if ( -r /etc/shells ) then + complete setenv p@1@e@ n@DISPLAY@\$hosts@: n@SHELL@'`cat /etc/shells`'@ + else + complete setenv p@1@e@ n@DISPLAY@\$hosts@: + endif + complete unsetenv n/*/e/ + + if (-r $HOME/.mailrc) then + complete mail c/-/"(e i f n s u v)"/ c/*@/\$hosts/ \ + c@+@F:$HOME/Mail@ C@[./\$~]@f@ n/-s/x:''/ \ + n@-u@T:$_maildir@ n/-f/f/ \ + n@*@'`sed -n s/alias//p $HOME/.mailrc | tr -s " " " " | cut -f 2`'@ + else + complete mail c/-/"(e i f n s u v)"/ c/*@/\$hosts/ \ + c@+@F:$HOME/Mail@ C@[./\$~]@f@ n/-s/x:''/ \ + n@-u@T:$_maildir@ n/-f/f/ n/*/u/ + endif + + complete man n@1@'`\ls -1 /usr/man/man1 | sed s%\\.1.\*\$%%`'@ \ + n@2@'`\ls -1 /usr/man/man2 | sed s%\\.2.\*\$%%`'@ \ + n@3@'`\ls -1 /usr/man/man3 | sed s%\\.3.\*\$%%`'@ \ + n@4@'`\ls -1 /usr/man/man4 | sed s%\\.4.\*\$%%`'@ \ + n@5@'`\ls -1 /usr/man/man5 | sed s%\\.5.\*\$%%`'@ \ + n@6@'`\ls -1 /usr/man/man6 | sed s%\\.6.\*\$%%`'@ \ + n@7@'`\ls -1 /usr/man/man7 | sed s%\\.7.\*\$%%`'@ \ + n@8@'`\ls -1 /usr/man/man8 | sed s%\\.8.\*\$%%`'@ \ + n@9@'`[ -r /usr/man/man9 ] && \ls -1 /usr/man/man9 | sed s%\\.9.\*\$%%`'@ \ + n@0@'`[ -r /usr/man/man0 ] && \ls -1 /usr/man/man0 | sed s%\\.0.\*\$%%`'@ \ + n@new@'`[ -r /usr/man/mann ] && \ls -1 /usr/man/mann | sed s%\\.n.\*\$%%`'@ \ + n@old@'`[ -r /usr/man/mano ] && \ls -1 /usr/man/mano | sed s%\\.o.\*\$%%`'@ \ +n@local@'`[ -r /usr/man/manl ] && \ls -1 /usr/man/manl | sed s%\\.l.\*\$%%`'@ \ +n@public@'`[ -r /usr/man/manp ]&& \ls -1 /usr/man/manp | sed s%\\.p.\*\$%%`'@ \ + c/-/"(- f k P s t)"/ n/-f/c/ n/-k/x:''/ n/-P/d/ \ + N@-P@'`\ls -1 $:-1/man? | sed s%\\..\*\$%%`'@ n/*/c/ + + complete ps c/-t/x:''/ c/-/"(a c C e g k l S t u v w x)"/ \ + n/-k/x:''/ N/-k/x:''/ n/*/x:''/ + complete compress c/-/"(c f v b)"/ n/-b/x:''/ n/*/f:^*.Z/ + complete uncompress c/-/"(c f v)"/ n/*/f:*.Z/ + + complete xhost c/[+-]/\$hosts/ n/*/\$hosts/ + + # these conform to the latest GNU versions available at press time ... + + complete emacs c/-/"(batch d f funcall i insert kill l load \ + no-init-file nw q t u user)"/ c/+/x:''/ \ + n/-d/x:''/ n/-f/x:''/ n/-i/f/ \ + n@-l@F:$_elispdir@ n/-t/x:''/ \ + n/-u/u/ n/*/f:^*[\#~]/ + + complete gzcat c/--/"(force help license quiet version)"/ \ + c/-/"(f h L q V -)"/ n/*/f:*.{gz,Z,z,zip}/ + complete gzip c/--/"(stdout to-stdout decompress uncompress \ + force help list license no-name quiet recurse \ + suffix test verbose version fast best)"/ \ + c/-/"(c d f h l L n q r S t v V 1 2 3 4 5 6 7 8 9 -)"/\ + n/{-S,--suffix}/x:''/ \ + n/{-d,--{de,un}compress}/f:*.{gz,Z,z,zip,taz,tgz}/ \ + N/{-d,--{de,un}compress}/f:*.{gz,Z,z,zip,taz,tgz}/ \ + n/*/f:^*.{gz,Z,z,zip,taz,tgz}/ + complete {gunzip,ungzip} c/--/"(stdout to-stdout force help list license \ + no-name quiet recurse suffix test verbose version)"/ \ + c/-/"(c f h l L n q r S t v V -)"/ \ + n/{-S,--suffix}/x:''/ \ + n/*/f:*.{gz,Z,z,zip,taz,tgz}/ + complete zgrep c/-*A/x:'<#_lines_after>'/ c/-*B/x:'<#_lines_before>'/\ + c/-/"(A b B c C e f h i l n s v V w x)"/ \ + p/1/x:''/ \ + n/-*e/x:''/ n/-*f/f/ n/*/f/ + complete zegrep c/-*A/x:'<#_lines_after>'/ c/-*B/x:'<#_lines_before>'/\ + c/-/"(A b B c C e f h i l n s v V w x)"/ \ + p/1/x:''/ \ + n/-*e/x:''/ n/-*f/f/ n/*/f/ + complete zfgrep c/-*A/x:'<#_lines_after>'/ c/-*B/x:'<#_lines_before>'/\ + c/-/"(A b B c C e f h i l n s v V w x)"/ \ + p/1/x:''/ \ + n/-*e/x:''/ n/-*f/f/ n/*/f/ + + complete znew c/-/"(f t v 9 P K)"/ n/*/f:*.Z/ + complete zmore n/*/f:*.{gz,Z,z,zip}/ + complete zfile n/*/f:*.{gz,Z,z,zip,taz,tgz}/ + complete ztouch n/*/f:*.{gz,Z,z,zip,taz,tgz}/ + complete zforce n/*/f:^*.{gz,tgz}/ + + complete grep c/-*A/x:'<#_lines_after>'/ c/-*B/x:'<#_lines_before>'/\ + c/-/"(A b B c C e f h i l n s v V w x)"/ \ + p/1/x:''/ \ + n/-*e/x:''/ n/-*f/f/ n/*/f/ + complete egrep c/-*A/x:'<#_lines_after>'/ c/-*B/x:'<#_lines_before>'/\ + c/-/"(A b B c C e f h i l n s v V w x)"/ \ + p/1/x:''/ \ + n/-*e/x:''/ n/-*f/f/ n/*/f/ + complete fgrep c/-*A/x:'<#_lines_after>'/ c/-*B/x:'<#_lines_before>'/\ + c/-/"(A b B c C e f h i l n s v V w x)"/ \ + p/1/x:''/ \ + n/-*e/x:''/ n/-*f/f/ n/*/f/ + + complete users c/--/"(help version)"/ p/1/x:''/ + complete who c/--/"(heading mesg idle count help message version \ + writable)"/ c/-/"(H T w i u m q s -)"/ \ + p/1/x:''/ n/am/"(i)"/ n/are/"(you)"/ + + complete chown c/--/"(changes silent quiet verbose recursive help \ + version)"/ c/-/"(c f v R -)"/ C@[./\$~]@f@ c/*[.:]/g/ \ + n/-/u/. p/1/u/. n/*/f/ + complete chgrp c/--/"(changes silent quiet verbose recursive help \ + version)"/ c/-/"(c f v R -)"/ n/-/g/ p/1/g/ n/*/f/ + + complete cat c/--/"(number-nonblank number squeeze-blank show-all \ + show-nonprinting show-ends show-tabs help version)"/ \ + c/-/"(b e n s t u v A E T -)"/ n/*/f/ +if ($?traditional_cp_mv_complete) then + complete mv c/--/"(backup force interactive update verbose suffix \ + version-control help version)"/ \ + c/-/"(b f i u v S V -)"/ \ + n/{-S,--suffix}/x:''/ \ + n/{-V,--version-control}/"(t numbered nil existing \ + never simple)"/ n/-/f/ N/-/d/ p/1/f/ p/2/d/ n/*/f/ + complete cp c/--/"(archive backup no-dereference force interactive \ + link preserve symbolic-link update verbose parents \ + one-file-system recursive suffix version-control help \ + version)"/ c/-/"(a b d f i l p r s u v x P R S V -)"/ \ + n/-*r/d/ n/{-S,--suffix}/x:''/ \ + n/{-V,--version-control}/"(t numbered nil existing \ + never simple)"/ n/-/f/ N/-/d/ p/1/f/ p/2/d/ n/*/f/ +else + complete mv c/--/"(backup force interactive update verbose suffix \ + version-control help version)"/ \ + c/-/"(b f i u v S V -)"/ \ + n/{-S,--suffix}/x:''/ \ + n/{-V,--version-control}/"(t numbered nil existing \ + never simple)"/ n/-/f/ N/-/d/ n/*/f/ + complete cp c/--/"(archive backup no-dereference force interactive \ + link preserve symbolic-link update verbose parents \ + one-file-system recursive suffix version-control help \ + version)"/ c/-/"(a b d f i l p r s u v x P R S V -)"/ \ + n/-*r/d/ n/{-S,--suffix}/x:''/ \ + n/{-V,--version-control}/"(t numbered nil existing \ + never simple)"/ n/-/f/ N/-/d/ n/*/f/ +endif + complete ln c/--/"(backup directory force interactive symbolic \ + verbose suffix version-control help version)"/ \ + c/-/"(b d F f i s v S V -)"/ \ + n/{-S,--suffix}/x:''/ \ + n/{-V,--version-control}/"(t numbered nil existing \ + never simple)"/ n/-/f/ N/-/x:''/ \ + p/1/f/ p/2/x:''/ + complete touch c/--/"(date file help time version)"/ \ + c/-/"(a c d f m r t -)"/ \ + n/{-d,--date}/x:''/ \ + c/--time/"(access atime mtime modify use)"/ \ + n/{-r,--file}/f/ n/-t/x:''/ n/*/f/ + complete mkdir c/--/"(parents help version mode)"/ c/-/"(p m -)"/ \ + n/{-m,--mode}/x:''/ n/*/d/ + complete rmdir c/--/"(parents help version)"/ c/-/"(p -)"/ n/*/d/ + + complete tar c/-[Acru]*/"(b B C f F g G h i l L M N o P \ + R S T v V w W X z Z)"/ \ + c/-[dtx]*/"( B C f F g G i k K m M O p P \ + R s S T v w x X z Z)"/ \ + p/1/"(A c d r t u x -A -c -d -r -t -u -x \ + --catenate --concatenate --create --diff --compare \ + --delete --append --list --update --extract --get)"/ \ + c/--/"(catenate concatenate create diff compare \ + delete append list update extract get atime-preserve \ + block-size read-full-blocks directory checkpoint file \ + force-local info-script new-volume-script incremental \ + listed-incremental dereference ignore-zeros \ + ignore-failed-read keep-old-files starting-file \ + one-file-system tape-length modification-time \ + multi-volume after-date newer old-archive portability \ + to-stdout same-permissions preserve-permissions \ + absolute-paths preserve record-number remove-files \ + same-order preserve-order same-owner sparse \ + files-from null totals verbose label version \ + interactive confirmation verify exclude exclude-from \ + compress uncompress gzip ungzip use-compress-program \ + block-compress)"/ \ + c/-/"(b B C f F g G h i k K l L m M N o O p P R s S \ + T v V w W X z Z 0 1 2 3 4 5 6 7 -)"/ \ + n/-c*f/x:''/ \ + n/{-[Adrtux]*f,--file}/f:*.tar/ \ + N/{-x*f,--file}/'`tar -tf $:-1`'/ \ + n/--use-compress-program/c/ \ + n/{-b,--block-size}/x:''/ \ + n/{-V,--label}/x:''/ \ + n/{-N,--{after-date,newer}}/x:''/ \ + n/{-L,--tape-length}/x:''/ \ + n/{-C,--directory}/d/ \ + N/{-C,--directory}/'`\ls $:-1`'/ \ + n/-[0-7]/"(l m h)"/ + + # BSD 4.3 filesystems + complete mount c/-/"(a h v t r)"/ n/-h/\$hosts/ n/-t/"(4.2 nfs)"/ \ + n@*@'`cut -d " " -f 2 /etc/fstab`'@ + complete umount c/-/"(a h v t)"/ n/-h/\$hosts/ n/-t/"(4.2 nfs)"/ \ + n/*/'`mount | cut -d " " -f 3`'/ + # BSD 4.2 filesystems + #complete mount c/-/"(a h v t r)"/ n/-h/\$hosts/ n/-t/"(ufs nfs)"/ \ + # n@*@'`cut -d ":" -f 2 /etc/fstab`'@ + #complete umount c/-/"(a h v t)"/ n/-h/\$hosts/ n/-t/"(ufs nfs)"/ \ + # n/*/'`mount | cut -d " " -f 3`'/ + + # these deal with NIS (formerly YP); if it's not running you don't need 'em + complete domainname p@1@D:$_ypdir@" " n@*@n@ + complete ypcat c@-@"(d k t x)"@ n@-x@n@ n@-d@D:$_ypdir@" " \ + N@-d@\`\\ls\ -1\ $_ypdir/\$:-1\ \|\ sed\ -n\ s%\\\\.pag\\\$%%p\`@ \ + n@*@\`\\ls\ -1\ $_ypdir/$_domain\ \|\ sed\ -n\ s%\\\\.pag\\\$%%p\`@ + complete ypmatch c@-@"(d k t x)"@ n@-x@n@ n@-d@D:$_ypdir@" " \ + N@-d@x:''@ n@-@x:''@ p@1@x:''@ \ + n@*@\`\\ls\ -1\ $_ypdir/$_domain\ \|\ sed\ -n\ s%\\\\.pag\\\$%%p\`@ + complete ypwhich c@-@"(d m t x V1 V2)"@ n@-x@n@ n@-d@D:$_ypdir@" " \ + n@-m@\`\\ls\ -1\ $_ypdir/$_domain\ \|\ sed\ -n\ s%\\\\.pag\\\$%%p\`@ \ + N@-m@n@ n@*@\$hosts@ + + # there's no need to clutter the user's shell with these + unset _elispdir _maildir _ypdir _domain + + complete make \ + 'n/-f/f/' \ + 'c/*=/f/' \ + 'n@*@`cat -s GNUmakefile Makefile makefile |& sed -n -e "/No such file/d" -e "/^[^ #].*:/s/:.*//p"`@' + + if ( -f /etc/printcap ) then + set printers=(`sed -n -e "/^[^ #].*:/s/:.*//p" /etc/printcap`) + + complete lpr 'c/-P/$printers/' + complete lpq 'c/-P/$printers/' + complete lprm 'c/-P/$printers/' + complete lpquota 'p/1/(-Qprlogger)/' 'c/-P/$printers/' + complete dvips 'c/-P/$printers/' 'n/-o/f:*.{ps,PS}/' 'n/*/f:*.dvi/' + endif + +# New +if (! $?no_new_complete) then + uncomplete vi + complete {vi,vim,gvim,nvi,elvis} n/*/f:^*.{o,a,so,sa,aux,dvi,log,fig,bbl,blg,bst,idx,ilg,ind,toc}/ + complete {ispell,spell,spellword} 'n@-d@`ls /usr/lib/ispell/*.aff | sed -e "s/\.aff//" `@' 'n/*/f:^*.{o,a,so,sa,aux,dvi,log,fig,bbl,blg,bst,idx,ilg,ind,toc}/' + complete mutt 'n/-[ai]/f/' 'n/-c/u/' c@=@F:$HOME/Mail/@ \ + 'n/-s/x:\/' 'n/[^-]/u/' + complete elm 'n/-[Ai]/f/' 'c@=@F:$HOME/Mail/@' 'n/-s/x:\/' + complete ncftp 'n@*@`sed -e '1,2d' $HOME/.ncftp/bookmarks | cut -f 1,2 -d "," | tr "," "\012" | sort | uniq ` '@ + complete bibtex 'n@*@`ls *.aux | sed -e "s/\.aux//"`'@ + complete dvi2tty n/*/f:*.dvi/ # Only files that match *.dvi + complete {xpdf,acroread} 'n/*/f:*.pdf/' + complete {gv,ghostview} 'n/*/f:*.{ps,eps,epsi}/' + complete enscript \ + 'c/--/(columns= pages= header= no-header truncate-lines \ + line-numbers setpagedevice= escapes font= \ + header-font= fancy-header no-job-header \ + highlight-bars indent= filter= borders page-prefeed \ + no-page-prefeed lineprinter lines-per-page= mail \ + media= copies= newline= output= missing-characters \ + printer= quiet silent landscape portrait \ + baselineskip= statusdict= title= tabsize= underlay= \ + verbose version encoding pass-through download-font= \ + filter-stdin= help highlight-bar-gray= list-media \ + list-options non-printable-format= page-label-format= \ + printer-options= ul-angle= ul-font= ul-gray= \ + ul-position= ul-style= \ + )/' +endif # ! $?no_new_complete + +# Debian specific +if (! $?no_debian_complete) then +complete dpkg 'c/--{admindir,instdir,root}=/d/' \ + 'c/--debug=/n/' \ + 'c/--{admindir,debug,instdir,root}/(=)//' \ + 'c/--/(admindir= debug= instdir= root= \ + assert-support-predepends assert-working-epoch \ + audit auto-deconfigure clear-avail \ + compare-versions configure contents control \ + extract force-bad-path field \ + force-configure-any force-conflicts \ + force-depends force-depends-version force-help \ + force-hold force-non-root \ + force-overwrite-diverted \ + force-remove-essential force-remove-reinstreq \ + forget-old-unavail fsys-tarfile get-selections \ + help ignore-depends info install largemem \ + license list listfiles merge-avail no-act \ + pending predep-package print-architecture \ + print-gnu-build-architecture \ + print-installation-architecture print-avail \ + purge record-avail recursive refuse-downgrade \ + remove search set-selections selected-only \ + skip-same-version smallmem status unpack \ + update-avail version vextract \ + )//' \ + 'n/*/f:*.deb'/ +complete dpkg-deb 'c/--{build}=/d/' \ + 'c/--/"( build contents info field control extract \ + vextract fsys-tarfile help version \ + license )"' \ + 'n/*/f:*.deb/' +endif # ! $?no_debian_complete + + unset noglob + unset complete + unset traditional_complete + unset traditional_cp_mv_complete + unset traditional_zcat_complete + unset traditional_nm_complete + unset traditilnal_tex_complete + unset traditional_find_complete + unset traditional_configure_complete + unset traditional_rm_complete + unset foolproof_rm_complete + unset no_new_complete + unset no_debian_complete +endif + +end: + onintr + diff --git a/tests/examplefiles/test.xsl b/tests/examplefiles/test.xsl new file mode 100644 index 0000000..590bb04 --- /dev/null +++ b/tests/examplefiles/test.xsl @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examplefiles/type.lisp b/tests/examplefiles/type.lisp new file mode 100644 index 0000000..9c76937 --- /dev/null +++ b/tests/examplefiles/type.lisp @@ -0,0 +1,1202 @@ +;;;; TYPEP und Verwandtes +;;;; Michael Stoll, 21. 10. 1988 +;;;; Bruno Haible, 10.6.1989 +;;;; Sam Steingold 2000-2005 + +;;; Datenstrukturen für TYPEP: +;;; - Ein Type-Specifier-Symbol hat auf seiner Propertyliste unter dem +;;; Indikator SYS::TYPE-SYMBOL eine Funktion von einem Argument, die +;;; testet, ob ein Objekt vom richtigen Typ ist. +;;; - Ein Symbol, das eine Type-Specifier-Liste beginnen kann, hat auf seiner +;;; Propertyliste unter dem Indikator SYS::TYPE-LIST eine Funktion von +;;; einem Argument für das zu testende Objekt und zusätzlichen Argumenten +;;; für die Listenelemente. +;;; - Ein Symbol, das als Typmacro definiert wurde, hat auf seiner Property- +;;; liste unter dem Indikator SYSTEM::DEFTYPE-EXPANDER den zugehörigen +;;; Expander: eine Funktion, die den zu expandierenden Type-Specifier (eine +;;; mindestens einelementige Liste) als Argument bekommt. + +(in-package "EXT") +(export '(type-expand)) +(in-package "SYSTEM") + +; vorläufig, solange bis clos.lisp geladen wird: +(eval-when (eval) + (predefun clos::built-in-class-p (object) (declare (ignore object)) nil)) +(unless (fboundp 'clos::class-name) + (defun clos::class-name (c) (declare (ignore c)) nil) +) + +(defun typespec-error (fun type) + (error-of-type 'error + (TEXT "~S: invalid type specification ~S") + fun type +) ) + +;; ============================================================================ + +;; return the CLOS class named by TYPESPEC or NIL +(defun clos-class (typespec) + (let ((cc (get typespec 'CLOS::CLOSCLASS))) + (when (and cc (clos::defined-class-p cc) (eq (clos:class-name cc) typespec)) + cc))) + +;;; TYPEP, CLTL S. 72, S. 42-51 +(defun typep (x y &optional env &aux f) ; x = Objekt, y = Typ + (declare (ignore env)) + (setq y (expand-deftype y)) + (cond + ((symbolp y) + (cond ((setq f (get y 'TYPE-SYMBOL)) (funcall f x)) + ((setq f (get y 'TYPE-LIST)) (funcall f x)) + ((setq f (get y 'DEFSTRUCT-DESCRIPTION)) (ds-typep x y f)) + ((setq f (clos-class y)) + ; It's not worth handling structure classes specially here. + (clos::typep-class x f)) + (t (typespec-error 'typep y)) + ) ) + ((and (consp y) (symbolp (first y))) + (cond + ((and (eq (first y) 'SATISFIES) (eql (length y) 2)) + (unless (symbolp (second y)) + (error-of-type 'error + (TEXT "~S: argument to SATISFIES must be a symbol: ~S") + 'typep (second y) + ) ) + (if (funcall (symbol-function (second y)) x) t nil) + ) + ((eq (first y) 'MEMBER) + (if (member x (rest y)) t nil) + ) + ((and (eq (first y) 'EQL) (eql (length y) 2)) + (eql x (second y)) + ) + ((and (eq (first y) 'NOT) (eql (length y) 2)) + (not (typep x (second y))) + ) + ((eq (first y) 'AND) + (dolist (type (rest y) t) + (unless (typep x type) (return nil)) + ) ) + ((eq (first y) 'OR) + (dolist (type (rest y) nil) + (when (typep x type) (return t)) + ) ) + ((setq f (get (first y) 'TYPE-LIST)) (apply f x (rest y))) + (t (typespec-error 'typep y)) + ) ) + ((clos::defined-class-p y) (clos::typep-class x y)) + ((clos::eql-specializer-p y) (eql x (clos::eql-specializer-singleton y))) + ((encodingp y) (charset-typep x y)) + (t (typespec-error 'typep y)) +) ) + +;; ---------------------------------------------------------------------------- + +;; UPGRADED-ARRAY-ELEMENT-TYPE is a lattice homomorphism, see +;; ANSI CL 15.1.2.1. +(defun upgraded-array-element-type (type &optional environment) + (declare (ignore environment)) + ;; see array.d + (case type + ((BIT) 'BIT) + ((CHARACTER) 'CHARACTER) + ((T) 'T) + ((NIL) 'NIL) + (t (if (subtypep type 'NIL) + 'NIL + (multiple-value-bind (low high) (sys::subtype-integer type) + ; Es gilt (or (null low) (subtypep type `(INTEGER ,low ,high))) + (if (and (integerp low) (not (minusp low)) (integerp high)) + (let ((l (integer-length high))) + ; Es gilt (subtypep type `(UNSIGNED-BYTE ,l)) + (cond ((<= l 1) 'BIT) + ((<= l 2) '(UNSIGNED-BYTE 2)) + ((<= l 4) '(UNSIGNED-BYTE 4)) + ((<= l 8) '(UNSIGNED-BYTE 8)) + ((<= l 16) '(UNSIGNED-BYTE 16)) + ((<= l 32) '(UNSIGNED-BYTE 32)) + (t 'T))) + (if (subtypep type 'CHARACTER) + 'CHARACTER + 'T))))))) + +;; ---------------------------------------------------------------------------- + +;; UPGRADED-COMPLEX-PART-TYPE is a lattice homomorphism, see +;; HyperSpec/Body/fun_complex.html and HyperSpec/Body/syscla_complex.html, +;; and an idempotent. Therefore +;; (subtypep (upgraded-complex-part-type T1) (upgraded-complex-part-type T2)) +;; is equivalent to +;; (subtypep T1 (upgraded-complex-part-type T2)) +;; (Proof: Let U T be an abbreviation for (upgraded-complex-part-type T). +;; If U T1 <= U T2, then T1 <= U T1 <= U T2. +;; If T1 <= U T2, then by homomorphism U T1 <= U U T2 = U T2.) +;; +;; For _any_ CL implementation, you could define +;; (defun upgraded-complex-part-type (type) 'REAL) +;; Likewise for _any_ CL implementation, you could define +;; (defun upgraded-complex-part-type (type) type) +;; or - again for _any_ CL implementation: +;; (defun upgraded-complex-part-type (type) +;; (cond ((subtypep type 'NIL) 'NIL) +;; ((subtypep type 'SHORT-FLOAT) 'SHORT-FLOAT) +;; ((subtypep type 'SINGLE-FLOAT) 'SINGLE-FLOAT) +;; ((subtypep type 'DOUBLE-FLOAT) 'DOUBLE-FLOAT) +;; ((subtypep type 'LONG-FLOAT) 'LONG-FLOAT) +;; ((subtypep type 'RATIONAL) 'RATIONAL) +;; ((subtypep type 'REAL) 'REAL) +;; (t (error ...)))) +;; The reason is that a complex number is immutable: no setters for the +;; realpart and imagpart exist. +;; +;; We choose the second implementation because it allows the most precise +;; type inference. +(defun upgraded-complex-part-type (type &optional environment) + (declare (ignore environment)) + (if (subtypep type 'REAL) + type + (error-of-type 'error + (TEXT "~S: type ~S is not a subtype of ~S") + 'upgraded-complex-part-type type 'real))) + +;; ---------------------------------------------------------------------------- + +;; Macros for defining the various built-in "atomic type specifier"s and +;; "compound type specifier"s. The following macros add information for both +;; the TYPEP function above and the c-TYPEP in the compiler. + +; Alist symbol -> funname, used by the compiler. +(defparameter c-typep-alist1 '()) +; Alist symbol -> lambdabody, used by the compiler. +(defparameter c-typep-alist2 '()) +; Alist symbol -> expander function, used by the compiler. +(defparameter c-typep-alist3 '()) + +; (def-atomic-type symbol function-name) +; defines an atomic type. The function-name designates a function taking one +; argument and returning a generalized boolean value. It can be either a +; symbol or a lambda expression. +(defmacro def-atomic-type (symbol funname) + (let ((lambdap (and (consp funname) (eq (car funname) 'LAMBDA)))) + `(PROGN + (SETF (GET ',symbol 'TYPE-SYMBOL) + ,(if lambdap + `(FUNCTION ,(concat-pnames "TYPE-SYMBOL-" symbol) ,funname) + `(FUNCTION ,funname) + ) + ) + ,(if lambdap + `(SETQ C-TYPEP-ALIST2 + (NCONC C-TYPEP-ALIST2 (LIST (CONS ',symbol ',(cdr funname)))) + ) + `(SETQ C-TYPEP-ALIST1 + (NCONC C-TYPEP-ALIST1 (LIST (CONS ',symbol ',funname))) + ) + ) + ',symbol + ) +) ) + +; (def-compound-type symbol lambda-list (x) check-form typep-form c-typep-form) +; defines a compound type. The lambda-list is of the form (&optional ...) +; where the arguments come from the CDR of the type specifier. +; For typep-form, x is an object. +; For c-typep-form, x is a multiply evaluatable form (actually a gensym). +; check-form is a form performing error checking, may call `error'. +; typep-form should return a generalized boolean value. +; c-typep-form should produce a form returning a generalized boolean value. +(defmacro def-compound-type (symbol lambdalist (var) check-form typep-form c-typep-form) + `(PROGN + (SETF (GET ',symbol 'TYPE-LIST) + (FUNCTION ,(concat-pnames "TYPE-LIST-" symbol) + (LAMBDA (,var ,@lambdalist) + ,@(if check-form + `((MACROLET ((ERROR (&REST ERROR-ARGS) + (LIST* 'ERROR-OF-TYPE ''ERROR ERROR-ARGS) + )) + ,check-form + )) + ) + ,typep-form + ) ) ) + (SETQ C-TYPEP-ALIST3 + (NCONC C-TYPEP-ALIST3 + (LIST (CONS ',symbol + #'(LAMBDA (,var ,@lambdalist &REST ILLEGAL-ARGS) + (DECLARE (IGNORE ILLEGAL-ARGS)) + ,@(if check-form + `((MACROLET ((ERROR (&REST ERROR-ARGS) + (LIST 'PROGN + (LIST* 'C-WARN ERROR-ARGS) + '(THROW 'C-TYPEP NIL) + )) ) + ,check-form + )) + ) + ,c-typep-form + ) + ) ) ) ) + ',symbol + ) +) + +; CLtL1 p. 43 +(def-atomic-type ARRAY arrayp) +(def-atomic-type ATOM atom) +(def-atomic-type BASE-CHAR + #+BASE-CHAR=CHARACTER + characterp + #-BASE-CHAR=CHARACTER + (lambda (x) (and (characterp x) (base-char-p x))) +) +(def-atomic-type BASE-STRING + (lambda (x) + (and (stringp x) + (eq (array-element-type x) + #+BASE-CHAR=CHARACTER 'CHARACTER #-BASE-CHAR=CHARACTER 'BASE-CHAR +) ) ) ) +(def-atomic-type BIGNUM + (lambda (x) (and (integerp x) (not (fixnump x)))) +) +(def-atomic-type BIT + (lambda (x) (or (eql x 0) (eql x 1))) +) +(def-atomic-type BIT-VECTOR bit-vector-p) +(def-atomic-type BOOLEAN + (lambda (x) (or (eq x 'nil) (eq x 't))) +) +(def-atomic-type CHARACTER characterp) +(def-atomic-type COMPILED-FUNCTION compiled-function-p) +(def-atomic-type COMPLEX complexp) +(def-atomic-type CONS consp) +(def-atomic-type DOUBLE-FLOAT double-float-p) +(def-atomic-type ENCODING encodingp) +(def-atomic-type EXTENDED-CHAR + #+BASE-CHAR=CHARACTER + (lambda (x) (declare (ignore x)) nil) + #-BASE-CHAR=CHARACTER + (lambda (x) (and (characterp x) (not (base-char-p x)))) +) +(def-atomic-type FIXNUM fixnump) +(def-atomic-type FLOAT floatp) +(def-atomic-type FUNCTION functionp) +(def-atomic-type HASH-TABLE hash-table-p) +(def-atomic-type INTEGER integerp) +(def-atomic-type KEYWORD keywordp) +(def-atomic-type LIST listp) +#+LOGICAL-PATHNAMES +(def-atomic-type LOGICAL-PATHNAME logical-pathname-p) +(def-atomic-type LONG-FLOAT long-float-p) +(def-atomic-type NIL + (lambda (x) (declare (ignore x)) nil) +) +(def-atomic-type NULL null) +(def-atomic-type NUMBER numberp) +(def-atomic-type PACKAGE packagep) +(def-atomic-type PATHNAME pathnamep) +(def-atomic-type RANDOM-STATE random-state-p) +(def-atomic-type RATIO + (lambda (x) (and (rationalp x) (not (integerp x)))) +) +(def-atomic-type RATIONAL rationalp) +(def-atomic-type READTABLE readtablep) +(def-atomic-type REAL realp) +(def-atomic-type SEQUENCE sequencep) +(def-atomic-type SHORT-FLOAT short-float-p) +(def-atomic-type SIMPLE-ARRAY simple-array-p) +(def-atomic-type SIMPLE-BASE-STRING + (lambda (x) + (and (simple-string-p x) + (eq (array-element-type x) + #+BASE-CHAR=CHARACTER 'CHARACTER #-BASE-CHAR=CHARACTER 'BASE-CHAR +) ) ) ) +(def-atomic-type SIMPLE-BIT-VECTOR simple-bit-vector-p) +(def-atomic-type SIMPLE-STRING simple-string-p) +(def-atomic-type SIMPLE-VECTOR simple-vector-p) +(def-atomic-type SINGLE-FLOAT single-float-p) +(defun %standard-char-p (x) (and (characterp x) (standard-char-p x))) ; ABI +(def-atomic-type STANDARD-CHAR %standard-char-p) +(def-atomic-type CLOS:STANDARD-OBJECT clos::std-instance-p) +(def-atomic-type STREAM streamp) +(def-atomic-type FILE-STREAM file-stream-p) +(def-atomic-type SYNONYM-STREAM synonym-stream-p) +(def-atomic-type BROADCAST-STREAM broadcast-stream-p) +(def-atomic-type CONCATENATED-STREAM concatenated-stream-p) +(def-atomic-type TWO-WAY-STREAM two-way-stream-p) +(def-atomic-type ECHO-STREAM echo-stream-p) +(def-atomic-type STRING-STREAM string-stream-p) +(def-atomic-type STRING stringp) +(def-atomic-type STRING-CHAR characterp) +(def-atomic-type CLOS:STRUCTURE-OBJECT clos::structure-object-p) +(def-atomic-type SYMBOL symbolp) +(def-atomic-type T (lambda (x) (declare (ignore x)) t)) +;; foreign1.lisp is loaded after this file, +;; so these symbols are not external yet +#+ffi +(def-atomic-type ffi::foreign-function + (lambda (x) (eq 'ffi::foreign-function (type-of x)))) +#+ffi +(def-atomic-type ffi::foreign-variable + (lambda (x) (eq 'ffi::foreign-variable (type-of x)))) +#+ffi +(def-atomic-type ffi::foreign-address + (lambda (x) (eq 'ffi::foreign-address (type-of x)))) +;; see lispbibl.d (#define FOREIGN) and predtype.d (TYPE-OF): +#+(or unix ffi affi win32) +(def-atomic-type foreign-pointer + (lambda (x) (eq 'foreign-pointer (type-of x)))) +(def-atomic-type VECTOR vectorp) +(def-atomic-type PLIST + (lambda (x) (multiple-value-bind (length tail) (list-length-dotted x) + (and (null tail) (evenp length))))) + +(defmacro ensure-dim (type dim) + ;; make sure DIM is a valid dimension + `(unless (or (eq ,dim '*) (typep ,dim `(INTEGER 0 (,ARRAY-DIMENSION-LIMIT)))) + (error (TEXT "~S: dimension ~S is invalid") ',type ,dim))) + +(defmacro ensure-rank (type rank) + ;; make sure RANK is a valid rank + `(unless (typep ,rank `(INTEGER 0 (,ARRAY-RANK-LIMIT))) + (error (TEXT "~S: rank ~S is invalid") ',type ,rank))) + +; CLtL1 p. 46-50 +(defun c-typep-array (tester el-type dims x) + `(AND (,tester ,x) + ,@(if (eq el-type '*) + '() + `((EQUAL (ARRAY-ELEMENT-TYPE ,x) ',(upgraded-array-element-type el-type))) + ) + ,@(if (eq dims '*) + '() + (if (numberp dims) + `((EQL ,dims (ARRAY-RANK ,x))) + `((EQL ,(length dims) (ARRAY-RANK ,x)) + ,@(let ((i 0)) + (mapcap #'(lambda (dim) + (prog1 + (if (eq dim '*) + '() + `((EQL ',dim (ARRAY-DIMENSION ,x ,i))) + ) + (incf i) + ) ) + dims + ) ) + ) + ) ) + ) +) +(defun c-typep-vector (tester size x) + `(AND (,tester ,x) + ,@(if (eq size '*) + '() + `((EQL ',size (ARRAY-DIMENSION ,x 0))) + ) + ) +) +(defun typep-number-test (x low high test type) + (and (funcall test x) + (cond ((eq low '*)) + ((funcall test low) (<= low x)) + ((and (consp low) (null (rest low)) (funcall test (first low))) + (< (first low) x) + ) + (t (error-of-type 'error + #1=(TEXT "~S: argument to ~S must be *, ~S or a list of ~S: ~S") + 'typep type type type low + ) ) ) + (cond ((eq high '*)) + ((funcall test high) (>= high x)) + ((and (consp high) (null (rest high)) (funcall test (first high))) + (> (first high) x) + ) + (t (error-of-type 'error + #1# 'typep type type type high +) ) ) ) ) +(defun c-typep-number (caller tester low high x) + `(AND (,tester ,x) + ,@(cond ((eq low '*) '()) + ((funcall tester low) `((<= ,low ,x))) + ((and (consp low) (null (rest low)) (funcall tester (first low))) + `((< ,(first low) ,x)) + ) + (t (c-warn #1=(TEXT "~S: argument to ~S must be *, ~S or a list of ~S: ~S") + 'typep caller caller caller low + ) + (throw 'c-TYPEP nil) + ) ) + ,@(cond ((eq high '*) '()) + ((funcall tester high) `((>= ,high ,x))) + ((and (consp high) (null (rest high)) (funcall tester (first high))) + `((> ,(first high) ,x)) + ) + (t (c-warn #1# 'typep caller caller caller high) + (throw 'c-TYPEP nil) + ) ) + ) +) +(def-compound-type ARRAY (&optional (el-type '*) (dims '*)) (x) + (unless (eq dims '*) + (if (numberp dims) + (ensure-rank ARRAY dims) + (dolist (dim dims) (ensure-dim ARRAY dim)))) + (and (arrayp x) + (or (eq el-type '*) + (equal (array-element-type x) (upgraded-array-element-type el-type)) + ) + (or (eq dims '*) + (if (numberp dims) + (eql dims (array-rank x)) + (and (eql (length dims) (array-rank x)) + (every #'(lambda (a b) (or (eq a '*) (eql a b))) + dims (array-dimensions x) + ) ) ) ) ) + (c-typep-array 'ARRAYP el-type dims x) +) +(def-compound-type SIMPLE-ARRAY (&optional (el-type '*) (dims '*)) (x) + (unless (eq dims '*) + (if (numberp dims) + (ensure-rank SIMPLE-ARRAY dims) + (dolist (dim dims) (ensure-dim SIMPLE-ARRAY dim)))) + (and (simple-array-p x) + (or (eq el-type '*) + (equal (array-element-type x) (upgraded-array-element-type el-type)) + ) + (or (eq dims '*) + (if (numberp dims) + (eql dims (array-rank x)) + (and (eql (length dims) (array-rank x)) + (every #'(lambda (a b) (or (eq a '*) (eql a b))) + dims (array-dimensions x) + ) ) ) ) ) + (c-typep-array 'SIMPLE-ARRAY-P el-type dims x) +) +(def-compound-type VECTOR (&optional (el-type '*) (size '*)) (x) + (ensure-dim VECTOR size) + (and (vectorp x) + (or (eq el-type '*) + (equal (array-element-type x) (upgraded-array-element-type el-type)) + ) + (or (eq size '*) (eql (array-dimension x 0) size)) + ) + `(AND (VECTORP ,x) + ,@(if (eq el-type '*) + '() + `((EQUAL (ARRAY-ELEMENT-TYPE ,x) ',(upgraded-array-element-type el-type))) + ) + ,@(if (eq size '*) + '() + `((EQL (ARRAY-DIMENSION ,x 0) ',size)) + ) + ) +) +(def-compound-type SIMPLE-VECTOR (&optional (size '*)) (x) + (ensure-dim SIMLPE-VECTOR size) + (and (simple-vector-p x) + (or (eq size '*) (eql size (array-dimension x 0))) + ) + (c-typep-vector 'SIMPLE-VECTOR-P size x) +) +(def-compound-type COMPLEX (&optional (rtype '*) (itype rtype)) (x) + nil + (and (complexp x) + (or (eq rtype '*) + (typep (realpart x) (upgraded-complex-part-type rtype))) + (or (eq itype '*) + (typep (imagpart x) (upgraded-complex-part-type itype)))) + `(AND (COMPLEXP ,x) + ,@(if (eq rtype '*) + '() + `((TYPEP (REALPART ,x) ',(upgraded-complex-part-type rtype)))) + ,@(if (eq itype '*) + '() + `((TYPEP (IMAGPART ,x) ',(upgraded-complex-part-type itype)))))) +(def-compound-type INTEGER (&optional (low '*) (high '*)) (x) + nil + (typep-number-test x low high #'integerp 'INTEGER) + (c-typep-number 'INTEGER 'INTEGERP low high x) +) +(def-compound-type MOD (n) (x) + (unless (integerp n) + (error (TEXT "~S: argument to MOD must be an integer: ~S") + 'typep n + ) ) + (and (integerp x) (<= 0 x) (< x n)) + `(AND (INTEGERP ,x) (NOT (MINUSP ,x)) (< ,x ,n)) +) +(def-compound-type SIGNED-BYTE (&optional (n '*)) (x) + (unless (or (eq n '*) (integerp n)) + (error (TEXT "~S: argument to SIGNED-BYTE must be an integer or * : ~S") + 'typep n + ) ) + (and (integerp x) (or (eq n '*) (< (integer-length x) n))) + `(AND (INTEGERP ,x) + ,@(if (eq n '*) '() `((< (INTEGER-LENGTH ,x) ,n))) + ) +) +(def-compound-type UNSIGNED-BYTE (&optional (n '*)) (x) + (unless (or (eq n '*) (integerp n)) + (error (TEXT "~S: argument to UNSIGNED-BYTE must be an integer or * : ~S") + 'typep n + ) ) + (and (integerp x) + (not (minusp x)) + (or (eq n '*) (<= (integer-length x) n)) + ) + `(AND (INTEGERP ,x) (NOT (MINUSP ,x)) + ,@(if (eq n '*) '() `((<= (INTEGER-LENGTH ,x) ,n))) + ) +) +(def-compound-type REAL (&optional (low '*) (high '*)) (x) + nil + (typep-number-test x low high #'realp 'REAL) + (c-typep-number 'REAL 'REALP low high x) +) +(def-compound-type RATIONAL (&optional (low '*) (high '*)) (x) + nil + (typep-number-test x low high #'rationalp 'RATIONAL) + (c-typep-number 'RATIONAL 'RATIONALP low high x) +) +(def-compound-type FLOAT (&optional (low '*) (high '*)) (x) + nil + (typep-number-test x low high #'floatp 'FLOAT) + (c-typep-number 'FLOAT 'FLOATP low high x) +) +(def-compound-type SHORT-FLOAT (&optional (low '*) (high '*)) (x) + nil + (typep-number-test x low high #'short-float-p 'SHORT-FLOAT) + (c-typep-number 'SHORT-FLOAT 'SHORT-FLOAT-P low high x) +) +(def-compound-type SINGLE-FLOAT (&optional (low '*) (high '*)) (x) + nil + (typep-number-test x low high #'single-float-p 'SINGLE-FLOAT) + (c-typep-number 'SINGLE-FLOAT 'SINGLE-FLOAT-P low high x) +) +(def-compound-type DOUBLE-FLOAT (&optional (low '*) (high '*)) (x) + nil + (typep-number-test x low high #'double-float-p 'DOUBLE-FLOAT) + (c-typep-number 'DOUBLE-FLOAT 'DOUBLE-FLOAT-P low high x) +) +(def-compound-type LONG-FLOAT (&optional (low '*) (high '*)) (x) + nil + (typep-number-test x low high #'long-float-p 'LONG-FLOAT) + (c-typep-number 'LONG-FLOAT 'LONG-FLOAT-P low high x) +) +(def-compound-type STRING (&optional (size '*)) (x) + (ensure-dim STRING size) + (and (stringp x) + (or (eq size '*) (eql size (array-dimension x 0))) + ) + (c-typep-vector 'STRINGP size x) +) +(def-compound-type SIMPLE-STRING (&optional (size '*)) (x) + (ensure-dim SIMPLE-STRING size) + (and (simple-string-p x) + (or (eq size '*) (eql size (array-dimension x 0))) + ) + (c-typep-vector 'SIMPLE-STRING-P size x) +) +(def-compound-type BASE-STRING (&optional (size '*)) (x) + (ensure-dim BASE-STRING size) + (and (stringp x) + (or (eq size '*) (eql size (array-dimension x 0))) + ) + (c-typep-vector 'STRINGP size x) +) +(def-compound-type SIMPLE-BASE-STRING (&optional (size '*)) (x) + (ensure-dim SIMPLE-BASE-STRING size) + (and (simple-string-p x) + (or (eq size '*) (eql size (array-dimension x 0))) + ) + (c-typep-vector 'SIMPLE-STRING-P size x) +) +(def-compound-type BIT-VECTOR (&optional (size '*)) (x) + (ensure-dim BIT-VECTOR size) + (and (bit-vector-p x) + (or (eq size '*) (eql size (array-dimension x 0))) + ) + (c-typep-vector 'BIT-VECTOR-P size x) +) +(def-compound-type SIMPLE-BIT-VECTOR (&optional (size '*)) (x) + (ensure-dim SIMPLE-BIT-VECTOR size) + (and (simple-bit-vector-p x) + (or (eq size '*) (eql size (array-dimension x 0))) + ) + (c-typep-vector 'SIMPLE-BIT-VECTOR-P size x) +) +(def-compound-type CONS (&optional (car-type '*) (cdr-type '*)) (x) + nil + (and (consp x) + (or (eq car-type '*) (typep (car x) car-type)) + (or (eq cdr-type '*) (typep (cdr x) cdr-type)) + ) + `(AND (CONSP ,x) + ,@(if (eq car-type '*) '() `((TYPEP (CAR ,x) ',car-type))) + ,@(if (eq cdr-type '*) '() `((TYPEP (CDR ,x) ',cdr-type))) + ) +) + +(fmakunbound 'def-compound-type) + +;; ---------------------------------------------------------------------------- + +; Typtest ohne Gefahr einer Fehlermeldung. Für SIGNAL und HANDLER-BIND. +(defun safe-typep (x y &optional env) + (let ((*error-handler* + #'(lambda (&rest error-args) + (declare (ignore error-args)) + (return-from safe-typep (values nil nil)) + )) ) + (values (typep x y env) t) +) ) + +; Umwandlung eines "type for declaration" in einen "type for discrimination". +(defun type-for-discrimination (y &optional (notp nil) &aux f) + (cond ((symbolp y) + (cond ((get y 'TYPE-SYMBOL) y) + ((get y 'TYPE-LIST) y) + ((setq f (get y 'DEFTYPE-EXPANDER)) + (let* ((z (funcall f (list y))) + (zx (type-for-discrimination z notp))) + (if (eql zx z) y zx) + )) + (t y) + ) ) + ((and (consp y) (symbolp (first y))) + (case (first y) + ((SATISFIES MEMBER EQL) y) + (NOT + (let* ((z (second y)) + (zx (type-for-discrimination z (not notp)))) + (if (eql zx z) y `(NOT ,zx)) + )) + ((AND OR COMPLEX VALUES) + (let* ((z (rest y)) + (zx (mapcar #'(lambda (x) (type-for-discrimination x notp)) z))) + (if (every #'eql z zx) y (cons (first y) zx)) + )) + (FUNCTION + ;; (FUNCTION arg-types res-type) is somewhere between + ;; NIL and FUNCTION, but undecidable. + (if notp 'NIL 'FUNCTION) + ) + (t (cond ((get (first y) 'TYPE-LIST) y) + ((setq f (get (first y) 'DEFTYPE-EXPANDER)) + (let* ((z (funcall f y)) + (zx (type-for-discrimination z notp))) + (if (eql zx z) y zx) + )) + (t y) + ) ) ) ) + (t y) +) ) + +; Testet eine Liste von Werten auf Erfüllen eines Type-Specifiers. Für THE. +(defun %the (values type) ; ABI + (macrolet ((near-typep (objform typform) + ;; near-typep ist wie typep, nur dass das Objekt auch ein + ;; Read-Label sein darf. Das tritt z.B. auf bei + ;; (read-from-string "#1=#S(FOO :X #1#)") + ;; im Konstruktor MAKE-FOO. Die Implementation ist aber + ;; nicht gezwungen, bei fehlerhaftem THE zwingend einen + ;; Fehler zu melden, darum ist ein lascherer Typcheck hier + ;; erlaubt. + (let ((g (gensym))) + `(let ((,g ,objform)) + (or (typep ,g ,typform) (eq (type-of ,g) 'READ-LABEL)))))) + (if (and (consp type) (eq (car type) 'VALUES)) + ;; The VALUES type specifier is ill-defined in ANSI CL. + ;; + ;; There are two possibilities to define a VALUES type specifier in a + ;; sane way: + ;; - (EXACT-VALUES type1 ... [&optional ...]) describes the exact shape + ;; of the values list, as received by MULTIPLE-VALUE-LIST. + ;; For example, (EXACT-VALUES SYMBOL) is matched by (values 'a) but not + ;; by (values 'a 'b) or (values). + ;; - (ASSIGNABLE-VALUES type1 ... [&optional ...]) describes the values + ;; as received by a set of variables through MULTIPLE-VALUE-BIND or + ;; MULTIPLE-VALUE-SETQ. For example, (ASSIGNABLE-VALUES SYMBOL) is + ;; defined by whether + ;; (MULTIPLE-VALUE-BIND (var1) values (DECLARE (TYPE SYMBOL var1)) ...) + ;; is valid or not; therefore (ASSIGNABLE-VALUES SYMBOL) is matched by + ;; (values 'a) and (values 'a 'b) and (values). + ;; Note that &OPTIONAL is actually redundant here: + ;; (ASSIGNABLE-VALUES type1 ... &optional otype1 ...) + ;; is equivalent to + ;; (ASSIGNABLE-VALUES type1 ... (OR NULL otype1) ...) + ;; HyperSpec/Body/typspe_values.html indicates that VALUES means + ;; EXACT-VALUES; however, HyperSpec/Body/speope_the.html indicates that + ;; VALUES means ASSIGNABLE-VALUES. + ;; + ;; SBCL interprets the VALUES type specifier to mean EXACT-VALUES when + ;; it contains &OPTIONAL or &REST, but ASSIGNABLE-VALUES when it has + ;; only a tuple of type specifiers. This is utter nonsense, in particular + ;; because it makes (VALUES type1 ... typek &OPTIONAL) + ;; different from (VALUES type1 ... typek). + ;; + ;; Here we use the ASSIGNABLE-VALUES interpretation. + ;; In SUBTYPEP we just punt and don't assume any interpretation. + (let ((vals values) (types (cdr type))) + ;; required: + (loop + (when (or (atom types) (atom vals)) (return-from %the t)) + (when (memq (car types) lambda-list-keywords) (return)) + (unless (near-typep (pop vals) (pop types)) + (return-from %the nil))) + ;; &optional: + (when (and (consp types) (eq (car types) '&optional)) + (setq types (cdr types)) + (loop + (when (or (atom types) (atom vals)) (return-from %the t)) + (when (memq (car types) lambda-list-keywords) (return)) + (unless (near-typep (pop vals) (pop types)) + (return-from %the nil)))) + ;; &rest &key: + (case (car types) + (&rest + (setq types (cdr types)) + (when (atom types) (typespec-error 'the type)) + (unless (near-typep (pop vals) (pop types)) + (return-from %the nil))) + (&key) + (t (typespec-error 'the type))) + (if (eq (car types) '&key) + (progn + (setq types (cdr types)) + (when (oddp (length vals)) (return-from %the nil)) + (let ((keywords nil)) + (loop + (when (or (atom types) (atom vals)) (return-from %the t)) + (when (memq (car types) lambda-list-keywords) (return)) + (let ((item (pop types))) + (unless (and (listp item) (eql (length item) 2) + (symbolp (first item))) + (typespec-error 'the type)) + (let ((kw (symbol-to-keyword (first item)))) + (unless (near-typep (getf vals kw) (second item)) + (return-from %the nil)) + (push kw keywords)))) + (if (and (consp types) (eq (car types) '&allow-other-keys)) + (setq types (cdr types)) + (unless (getf vals ':allow-other-keys) + (do ((L vals (cddr L))) + ((atom L)) + (unless (memq (car L) keywords) + (return-from %the nil))))))) + (when (consp types) (typespec-error 'the type))) + t) + (near-typep (if (consp values) (car values) nil) type)))) + +;;; =========================================================================== + +;; SUBTYPEP +(load "subtypep") + + +;; Returns the number of bytes that are needed to represent #\Null in a +;; given encoding. +(defun encoding-zeroes (encoding) + #+UNICODE + ;; this should use min_bytes_per_char for cache, not the hash table + (let ((name (ext:encoding-charset encoding)) + (table #.(make-hash-table :key-type '(or string symbol) :value-type 'fixnum + :test 'stablehash-equal :warn-if-needs-rehash-after-gc t + :initial-contents '(("UTF-7" . 1)))) + (tester #.(make-string 2 :initial-element (code-char 0)))) + (or (gethash name table) + (setf (gethash name table) + (- (length (ext:convert-string-to-bytes tester encoding)) + (length (ext:convert-string-to-bytes tester encoding + :end 1)))))) + #-UNICODE 1) + +;; Determines two values low,high such that +;; (subtypep type `(INTEGER ,low ,high)) +;; holds and low is as large as possible and high is as small as possible. +;; low = * means -infinity, high = * means infinity. +;; When (subtypep type 'INTEGER) is false, the values NIL,NIL are returned. +;; We need this function only for MAKE-ARRAY, UPGRADED-ARRAY-ELEMENT-TYPE and +;; OPEN and can therefore w.l.o.g. replace +;; type with `(OR ,type (MEMBER 0)) +#| ;; The original implementation calls canonicalize-type and then applies + ;; a particular SUBTYPE variant: + (defun subtype-integer (type) + (macrolet ((yes () '(return-from subtype-integer (values low high))) + (no () '(return-from subtype-integer nil)) + (unknown () '(return-from subtype-integer nil))) + (setq type (canonicalize-type type)) + (if (consp type) + (case (first type) + (MEMBER ; (MEMBER &rest objects) + ;; All elements must be of type INTEGER. + (let ((low 0) (high 0)) ; wlog! + (dolist (x (rest type) (yes)) + (unless (typep x 'INTEGER) (return (no))) + (setq low (min low x) high (max high x))))) + (OR ; (OR type*) + ;; Every type must be subtype of INTEGER. + (let ((low 0) (high 0)) ; wlog! + (dolist (type1 (rest type) (yes)) + (multiple-value-bind (low1 high1) (subtype-integer type1) + (unless low1 (return (no))) + (setq low (if (or (eq low '*) (eq low1 '*)) '* (min low low1)) + high (if (or (eq high '*) (eq high1 '*)) + '* (max high high1))))))) + (AND ; (AND type*) + ;; If one of the types is subtype of INTEGER, then yes, + ;; otherwise unknown. + (let ((low nil) (high nil)) + (dolist (type1 (rest type)) + (multiple-value-bind (low1 high1) (subtype-integer type1) + (when low1 + (if low + (setq low (if (eq low '*) low1 (if (eq low1 '*) low (max low low1))) + high (if (eq high '*) high1 (if (eq high1 '*) high (min high high1)))) + (setq low low1 high high1))))) + (if low + (progn + (when (and (numberp low) (numberp high) (not (<= low high))) + (setq low 0 high 0) ; type equivalent to NIL) + (yes)) + (unknown))))) + (setq type (list type))) + (if (eq (first type) 'INTEGER) + (let ((low (if (rest type) (second type) '*)) + (high (if (cddr type) (third type) '*))) + (when (consp low) + (setq low (first low)) + (when (numberp low) (incf low))) + (when (consp high) + (setq high (first high)) + (when (numberp high) (decf high))) + (when (and (numberp low) (numberp high) (not (<= low high))) ; type leer? + (setq low 0 high 0)) + (yes)) + (if (and (eq (first type) 'INTERVALS) (eq (second type) 'INTEGER)) + (let ((low (third type)) + (high (car (last type)))) + (when (consp low) + (setq low (first low)) + (when (numberp low) (incf low))) + (when (consp high) + (setq high (first high)) + (when (numberp high) (decf high))) + (yes)) + (unknown))))) +|# ;; This implementation inlines the (tail-recursive) canonicalize-type + ;; function. Its advantage is that it doesn't cons as much. + ;; (For example, (subtype-integer '(UNSIGNED-BYTE 8)) doesn't cons.) +(defun subtype-integer (type) + (macrolet ((yes () '(return-from subtype-integer (values low high))) + (no () '(return-from subtype-integer nil)) + (unknown () '(return-from subtype-integer nil))) + (setq type (expand-deftype type)) + (cond ((symbolp type) + (case type + (BIT (let ((low 0) (high 1)) (yes))) + (FIXNUM + (let ((low '#,most-negative-fixnum) + (high '#,most-positive-fixnum)) + (yes))) + ((INTEGER BIGNUM SIGNED-BYTE) + (let ((low '*) (high '*)) (yes))) + (UNSIGNED-BYTE + (let ((low 0) (high '*)) (yes))) + ((NIL) + (let ((low 0) (high 0)) (yes))) ; wlog! + (t (no)))) + ((and (consp type) (symbolp (first type))) + (unless (and (list-length type) (null (cdr (last type)))) + (typespec-error 'subtypep type)) + (case (first type) + (MEMBER ; (MEMBER &rest objects) + ;; All elements must be of type INTEGER. + (let ((low 0) (high 0)) ; wlog! + (dolist (x (rest type) (yes)) + (unless (typep x 'INTEGER) (return (no))) + (setq low (min low x) high (max high x))))) + (EQL ; (EQL object) + (let ((x (second type))) + (if (typep x 'INTEGER) + (let ((low (min 0 x)) (high (max 0 x))) (yes)) + (no)))) + (OR ; (OR type*) + ;; Every type must be subtype of INTEGER. + (let ((low 0) (high 0)) ; wlog! + (dolist (type1 (rest type) (yes)) + (multiple-value-bind (low1 high1) (subtype-integer type1) + (unless low1 (return (no))) + (setq low (if (or (eq low '*) (eq low1 '*)) + '* (min low low1)) + high (if (or (eq high '*) (eq high1 '*)) + '* (max high high1))))))) + (AND ; (AND type*) + ;; If one of the types is subtype of INTEGER, then yes, + ;; otherwise unknown. + (let ((low nil) (high nil)) + (dolist (type1 (rest type)) + (multiple-value-bind (low1 high1) (subtype-integer type1) + (when low1 + (if low + (setq low (if (eq low '*) low1 + (if (eq low1 '*) low + (max low low1))) + high (if (eq high '*) high1 + (if (eq high1 '*) high + (min high high1)))) + (setq low low1 + high high1))))) + (if low + (progn + (when (and (numberp low) (numberp high) + (not (<= low high))) + (setq low 0 high 0)) ; type equivalent to NIL + (yes)) + (unknown)))) + (INTEGER + (let ((low (if (rest type) (second type) '*)) + (high (if (cddr type) (third type) '*))) + (when (consp low) + (setq low (first low)) + (when (numberp low) (incf low))) + (when (consp high) + (setq high (first high)) + (when (numberp high) (decf high))) + (when (and (numberp low) (numberp high) (not (<= low high))) + (setq low 0 high 0)) ; type equivalent to NIL + (yes))) + (INTERVALS + (if (eq (second type) 'INTEGER) + (let ((low (third type)) + (high (car (last type)))) + (when (consp low) + (setq low (first low)) + (when (numberp low) (incf low))) + (when (consp high) + (setq high (first high)) + (when (numberp high) (decf high))) + (yes)) + (unknown))) + (MOD ; (MOD n) + (let ((n (second type))) + (unless (and (integerp n) (>= n 0)) + (typespec-error 'subtypep type)) + (if (eql n 0) + (no) + (let ((low 0) (high (1- n))) + (yes))))) + (SIGNED-BYTE ; (SIGNED-BYTE &optional s) + (let ((s (if (cdr type) (second type) '*))) + (if (eq s '*) + (let ((low '*) (high '*)) (yes)) + (progn + (unless (and (integerp s) (plusp s)) + (typespec-error 'subtypep type)) + (let ((n (ash 1 (1- s)))) ; (ash 1 *) == (expt 2 *) + (let ((low (- n)) (high (1- n))) + (yes))))))) + (UNSIGNED-BYTE ; (UNSIGNED-BYTE &optional s) + (let ((s (if (cdr type) (second type) '*))) + (if (eq s '*) + (let ((low 0) (high '*)) (yes)) + (progn + (unless (and (integerp s) (>= s 0)) + (typespec-error 'subtypep type)) + (let ((n (ash 1 s))) ; (ash 1 *) == (expt 2 *) + (let ((low 0) (high (1- n))) + (yes))))))) + (t (no)))) + ((clos::defined-class-p type) + (if (and (clos::built-in-class-p type) + (eq (get (clos:class-name type) 'CLOS::CLOSCLASS) type)) + (return-from subtype-integer + (subtype-integer (clos:class-name type))) + (no))) + ((clos::eql-specializer-p type) + (let ((x (clos::eql-specializer-singleton type))) + (if (typep x 'INTEGER) + (let ((low (min 0 x)) (high (max 0 x))) (yes)) + (no)))) + ((encodingp type) (no)) + (t (typespec-error 'subtypep type))))) + +#| TODO: Fix subtype-integer such that this works. +Henry Baker: + (defun type-null (x) + (values (and (eq 'bit (upgraded-array-element-type `(or bit ,x))) + (not (typep 0 x)) + (not (typep 1 x))) + t)) + (type-null '(and symbol number)) + (type-null '(and integer symbol)) + (type-null '(and integer character)) +|# + +;; Determines a sequence kind (an atom, as defined in defseq.lisp: one of +;; LIST - stands for LIST +;; VECTOR - stands for (VECTOR T) +;; STRING - stands for (VECTOR CHARACTER) +;; 1, 2, 4, 8, 16, 32 - stands for (VECTOR (UNSIGNED-BYTE n)) +;; 0 - stands for (VECTOR NIL)) +;; that indicates the sequence type meant by the given type. Other possible +;; return values are +;; SEQUENCE - denoting a type whose intersection with (OR LIST VECTOR) is not +;; subtype of LIST or VECTOR, or +;; NIL - indicating a type whose intersection with (OR LIST VECTOR) is empty. +;; When the type is (OR (VECTOR eltype1) ... (VECTOR eltypeN)), the chosen +;; element type is the smallest element type that contains all of eltype1 ... +;; eltypeN. +;; +;; User-defined sequence types are not supported here. +;; +;; This implementation inlines the (tail-recursive) canonicalize-type +;; function. Its advantage is that it doesn't cons as much. Also it employs +;; some heuristics and does not have the full power of SUBTYPEP. +(defun subtype-sequence (type) + (setq type (expand-deftype type)) + (cond ((symbolp type) + (case type + ((LIST CONS NULL) 'LIST) + ((NIL) 'NIL) + ((BIT-VECTOR SIMPLE-BIT-VECTOR) '1) + ((STRING SIMPLE-STRING BASE-STRING SIMPLE-BASE-STRING) 'STRING) + ((VECTOR SIMPLE-VECTOR ARRAY SIMPLE-ARRAY) 'VECTOR) + ((SEQUENCE) 'SEQUENCE) + (t 'NIL))) + ((and (consp type) (symbolp (first type))) + (unless (and (list-length type) (null (cdr (last type)))) + (typespec-error 'subtypep type)) + (case (first type) + (MEMBER ; (MEMBER &rest objects) + (let ((kind 'NIL)) + (dolist (x (rest type)) + (setq kind (sequence-type-union kind (type-of-sequence x)))) + kind)) + (EQL ; (EQL object) + (unless (eql (length type) 2) + (typespec-error 'subtypep type)) + (type-of-sequence (second type))) + (OR ; (OR type*) + (let ((kind 'NIL)) + (dolist (x (rest type)) + (setq kind (sequence-type-union kind (subtype-sequence x)))) + kind)) + (AND ; (AND type*) + (let ((kind 'SEQUENCE)) + (dolist (x (rest type)) + (setq kind (sequence-type-intersection kind (subtype-sequence x)))) + kind)) + ((SIMPLE-BIT-VECTOR BIT-VECTOR) ; (SIMPLE-BIT-VECTOR &optional size) + (when (cddr type) + (typespec-error 'subtypep type)) + '1) + ((SIMPLE-STRING STRING SIMPLE-BASE-STRING BASE-STRING) ; (SIMPLE-STRING &optional size) + (when (cddr type) + (typespec-error 'subtypep type)) + 'STRING) + (SIMPLE-VECTOR ; (SIMPLE-VECTOR &optional size) + (when (cddr type) + (typespec-error 'subtypep type)) + 'VECTOR) + ((VECTOR ARRAY SIMPLE-ARRAY) ; (VECTOR &optional el-type size), (ARRAY &optional el-type dimensions) + (when (cdddr type) + (typespec-error 'subtypep type)) + (let ((el-type (if (cdr type) (second type) '*))) + (if (eq el-type '*) + 'VECTOR + (let ((eltype (upgraded-array-element-type el-type))) + (cond ((eq eltype 'T) 'VECTOR) + ((eq eltype 'CHARACTER) 'STRING) + ((eq eltype 'BIT) '1) + ((and (consp eltype) (eq (first eltype) 'UNSIGNED-BYTE)) (second eltype)) + ((eq eltype 'NIL) '0) + (t (error (TEXT "~S is not up-to-date with ~S for element type ~S") + 'subtypep-sequence 'upgraded-array-element-type eltype))))))) + ((CONS) ; (CONS &optional cartype cdrtype) + (when (cdddr type) + (typespec-error 'subtypep type)) + 'LIST) + (t 'NIL))) + ((clos::defined-class-p type) + (if (and (clos::built-in-class-p type) + (eq (get (clos:class-name type) 'CLOS::CLOSCLASS) type)) + (subtype-sequence (clos:class-name type)) + 'NIL)) + ((clos::eql-specializer-p type) + (type-of-sequence (clos::eql-specializer-singleton type))) + (t 'NIL))) +(defun type-of-sequence (x) + (cond ((listp x) 'LIST) + ((vectorp x) + (let ((eltype (array-element-type x))) + (cond ((eq eltype 'T) 'VECTOR) + ((eq eltype 'CHARACTER) 'STRING) + ((eq eltype 'BIT) '1) + ((and (consp eltype) (eq (first eltype) 'UNSIGNED-BYTE)) (second eltype)) + ((eq eltype 'NIL) '0) + (t (error (TEXT "~S is not up-to-date with ~S for element type ~S") + 'type-of-sequence 'array-element-type eltype))))) + (t 'NIL))) +(defun sequence-type-union (t1 t2) + (cond ; Simple general rules. + ((eql t1 t2) t1) + ((eq t1 'NIL) t2) + ((eq t2 'NIL) t1) + ; Now the union of two different types. + ((or (eq t1 'SEQUENCE) (eq t2 'SEQUENCE)) 'SEQUENCE) + ((or (eq t1 'LIST) (eq t2 'LIST)) + ; union of LIST and a vector type + 'SEQUENCE) + ((or (eq t1 'VECTOR) (eq t2 'VECTOR)) 'VECTOR) + ((eql t1 0) t2) + ((eql t2 0) t1) + ((or (eq t1 'STRING) (eq t2 'STRING)) + ; union of STRING and an integer-vector type + 'VECTOR) + (t (max t1 t2)))) +(defun sequence-type-intersection (t1 t2) + (cond ; Simple general rules. + ((eql t1 t2) t1) + ((or (eq t1 'NIL) (eq t2 'NIL)) 'NIL) + ; Now the intersection of two different types. + ((eq t1 'SEQUENCE) t2) + ((eq t2 'SEQUENCE) t1) + ((or (eq t1 'LIST) (eq t2 'LIST)) + ; intersection of LIST and a vector type + 'NIL) + ((eq t1 'VECTOR) t2) + ((eq t2 'VECTOR) t1) + ((or (eql t1 0) (eql t2 0)) '0) + ((or (eq t1 'STRING) (eq t2 'STRING)) + ; intersection of STRING and an integer-vector type + '0) + (t (min t1 t2)))) + +;; ============================================================================ + +(defun type-expand (typespec &optional once-p) + (multiple-value-bind (expanded user-defined-p) + (expand-deftype typespec once-p) + (if user-defined-p (values expanded user-defined-p) + (cond ((symbolp typespec) + (cond ((or (get typespec 'TYPE-SYMBOL) (get typespec 'TYPE-LIST)) + (values typespec nil)) + ((or (get typespec 'DEFSTRUCT-DESCRIPTION) + (clos-class typespec)) + (values typespec nil)) + (t (typespec-error 'type-expand typespec)))) + ((and (consp typespec) (symbolp (first typespec))) + (case (first typespec) + ((SATISFIES MEMBER EQL NOT AND OR) (values typespec nil)) + (t (cond ((get (first typespec) 'TYPE-LIST) + (values typespec nil)) + (t (typespec-error 'type-expand typespec)))))) + ((clos::defined-class-p typespec) (values typespec nil)) + (t (typespec-error 'type-expand typespec)))))) + +;; ============================================================================ + +(unless (clos::funcallable-instance-p #'clos::class-name) + (fmakunbound 'clos::class-name)) diff --git a/tests/examplefiles/unicode.applescript b/tests/examplefiles/unicode.applescript new file mode 100644 index 0000000..8cc6c6f --- /dev/null +++ b/tests/examplefiles/unicode.applescript @@ -0,0 +1,5 @@ +set jp to "日本語" + +set ru to "Русский" + +jp & " and " & ru -- returns "日本語 and Русский" diff --git a/tests/examplefiles/while.pov b/tests/examplefiles/while.pov new file mode 100644 index 0000000..fb18245 --- /dev/null +++ b/tests/examplefiles/while.pov @@ -0,0 +1,13 @@ +#declare Index1 = 0; +#while(Index1 <= 9) + + #declare Index2 = 0; + #while(Index2 <= 19) + + sphere { , .5 } + + #declare Index2 = Index2 + 1; + #end + + #declare Index1 = Index1 + 1; +#end diff --git a/tests/examplefiles/zmlrpc.f90 b/tests/examplefiles/zmlrpc.f90 new file mode 100644 index 0000000..441497b --- /dev/null +++ b/tests/examplefiles/zmlrpc.f90 @@ -0,0 +1,798 @@ +!!$ +!!$ +!!$ MD2P4 +!!$ Multilevel Domain Decomposition Parallel Preconditioner Package for PSBLAS +!!$ for +!!$ Parallel Sparse BLAS v2.0 +!!$ (C) Copyright 2006 Salvatore Filippone University of Rome Tor Vergata +!!$ Alfredo Buttari University of Rome Tor Vergata +!!$ Daniela Di Serafino II University of Naples +!!$ Pasqua D'Ambra ICAR-CNR +!!$ +!!$ Redistribution and use in source and binary forms, with or without +!!$ modification, are permitted provided that the following conditions +!!$ are met: +!!$ 1. Redistributions of source code must retain the above copyright +!!$ notice, this list of conditions and the following disclaimer. +!!$ 2. Redistributions in binary form must reproduce the above copyright +!!$ notice, this list of conditions, and the following disclaimer in the +!!$ documentation and/or other materials provided with the distribution. +!!$ 3. The name of the MD2P4 group or the names of its contributors may +!!$ not be used to endorse or promote products derived from this +!!$ software without specific written permission. +!!$ +!!$ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +!!$ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +!!$ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +!!$ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE MD2P4 GROUP OR ITS CONTRIBUTORS +!!$ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +!!$ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +!!$ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +!!$ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +!!$ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +!!$ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +!!$ POSSIBILITY OF SUCH DAMAGE. +!!$ +!!$ +subroutine psb_zmlprc_aply(alpha,baseprecv,x,beta,y,desc_data,trans,work,info) + ! + ! Compute Y <- beta*Y + alpha*K^-1 X + ! where K is a multilevel preconditioner stored in baseprecv + ! + ! cfr.: Smith, Biorstad & Gropp + ! Domain Decomposition + ! Cambridge Univ. Press + ! + ! To each level I there corresponds a matrix A(I) and a preconditioner K(I) + ! + ! A notational difference: in the DD reference above the preconditioner for + ! a given level K(I) is written out as a sum over the subdomains + ! + ! SUM_k(R_k^T A_k R_k) + ! + ! whereas in this code the sum is implicit in the parallelization, + ! i.e. each process takes care of one subdomain, and for each level we have + ! as many subdomains as there are processes (except for the coarsest level where + ! we might have a replicated index space). Thus the sum apparently disappears + ! from our code, but only apparently, because it is implicit in the call + ! to psb_baseprc_aply. + ! + ! A bit of description of the baseprecv(:) data structure: + ! 1. Number of levels = NLEV = size(baseprecv(:)) + ! 2. baseprecv(ilev)%av(:) sparse matrices needed for the current level. + ! Includes: + ! 2.1.: baseprecv(ilev)%av(l_pr_) L factor of ILU preconditioners + ! 2.2.: baseprecv(ilev)%av(u_pr_) U factor of ILU preconditioners + ! 2.3.: baseprecv(ilev)%av(ap_nd_) Off-diagonal part of A for Jacobi sweeps + ! 2.4.: baseprecv(ilev)%av(ac_) Aggregated matrix of level ILEV + ! 2.5.: baseprecv(ilev)%av(sm_pr_t_) Smoother prolongator transpose; maps vectors + ! (ilev-1) ---> (ilev) + ! 2.6.: baseprecv(ilev)%av(sm_pr_) Smoother prolongator; maps vectors + ! (ilev) ---> (ilev-1) + ! Shouldn't we keep just one of them and handle transpose in the sparse BLAS? maybe + ! + ! 3. baseprecv(ilev)%desc_data comm descriptor for level ILEV + ! 4. baseprecv(ilev)%base_a Pointer (really a pointer!) to the base matrix + ! of the current level, i.e.: if ILEV=1 then A + ! else the aggregated matrix av(ac_); so we have + ! a unified treatment of residuals. Need this to + ! avoid passing explicitly matrix A to the + ! outer prec. routine + ! 5. baseprecv(ilev)%mlia The aggregation map from (ilev-1)-->(ilev) + ! if no smoother, it is used instead of sm_pr_ + ! 6. baseprecv(ilev)%nlaggr Number of aggregates on the various procs. + ! + + use psb_serial_mod + use psb_descriptor_type + use psb_prec_type + use psb_psblas_mod + use psb_penv_mod + use psb_const_mod + use psb_error_mod + use psb_penv_mod + implicit none + + type(psb_desc_type),intent(in) :: desc_data + type(psb_zbaseprc_type), intent(in) :: baseprecv(:) + complex(kind(1.d0)),intent(in) :: alpha,beta + complex(kind(1.d0)),intent(inout) :: x(:), y(:) + character :: trans + complex(kind(1.d0)),target :: work(:) + integer, intent(out) :: info + + + ! Local variables + integer :: n_row,n_col + complex(kind(1.d0)), allocatable :: tx(:),ty(:),t2l(:),w2l(:),& + & x2l(:),b2l(:),tz(:),tty(:) + character ::diagl, diagu + integer :: ictxt,np,me,i, isz, nrg,nr2l,err_act, iptype, int_err(5) + real(kind(1.d0)) :: omega + real(kind(1.d0)) :: t1, t2, t3, t4, t5, t6, t7, mpi_wtime + logical, parameter :: debug=.false., debugprt=.false. + integer :: ismth, nlev, ilev + external mpi_wtime + character(len=20) :: name, ch_err + + type psb_mlprec_wrk_type + complex(kind(1.d0)), pointer :: tx(:)=>null(),ty(:)=>null(),& + & x2l(:)=>null(),y2l(:)=>null(),& + & b2l(:)=>null(),tty(:)=>null() + end type psb_mlprec_wrk_type + type(psb_mlprec_wrk_type), pointer :: mlprec_wrk(:) + + interface psb_baseprc_aply + subroutine psb_zbaseprc_aply(alpha,prec,x,beta,y,desc_data,trans,work,info) + use psb_descriptor_type + use psb_prec_type + type(psb_desc_type),intent(in) :: desc_data + type(psb_zbaseprc_type), intent(in) :: prec + complex(kind(1.d0)),intent(inout) :: x(:), y(:) + complex(kind(1.d0)),intent(in) :: alpha,beta + character(len=1) :: trans + complex(kind(1.d0)),target :: work(:) + integer, intent(out) :: info + end subroutine psb_zbaseprc_aply + end interface + + name='psb_mlprc_aply' + info = 0 + call psb_erractionsave(err_act) + + + ictxt=desc_data%matrix_data(psb_ctxt_) + call psb_info(ictxt, me, np) + + nlev = size(baseprecv) + allocate(mlprec_wrk(nlev),stat=info) + if (info /= 0) then + call psb_errpush(4010,name,a_err='Allocate') + goto 9999 + end if + + + select case(baseprecv(2)%iprcparm(ml_type_)) + + case(no_ml_) + ! Should not really get here. + call psb_errpush(4010,name,a_err='no_ml_ in mlprc_aply?') + goto 9999 + + + case(add_ml_prec_) + + + ! + ! Additive is very simple. + ! 1. X(1) = Xext + ! 2. DO ILEV=2,NLEV + ! X(ILEV) = AV(PR_SM_T_)*X(ILEV-1) + ! 3. Y(ILEV) = (K(ILEV)**(-1))*X(ILEV) + ! 4. DO ILEV=NLEV-1,1,-1 + ! Y(ILEV) = AV(PR_SM_)*Y(ILEV+1) + ! 5. Yext = beta*Yext + Y(1) + ! + ! Note: level numbering reversed wrt ref. DD, i.e. + ! 1..NLEV <=> (j) <-> 0 + + + call psb_baseprc_aply(alpha,baseprecv(1),x,beta,y,& + & baseprecv(1)%base_desc,trans,work,info) + if(info /=0) goto 9999 + allocate(mlprec_wrk(1)%x2l(size(x)),mlprec_wrk(1)%y2l(size(y))) + mlprec_wrk(1)%x2l(:) = x(:) + + + do ilev = 2, nlev + n_row = baseprecv(ilev-1)%base_desc%matrix_data(psb_n_row_) + n_col = baseprecv(ilev-1)%desc_data%matrix_data(psb_n_col_) + nr2l = baseprecv(ilev)%desc_data%matrix_data(psb_n_col_) + nrg = baseprecv(ilev)%desc_data%matrix_data(psb_n_row_) + allocate(mlprec_wrk(ilev)%x2l(nr2l),mlprec_wrk(ilev)%y2l(nr2l),& + & mlprec_wrk(ilev)%tx(max(n_row,n_col)),& + & mlprec_wrk(ilev)%ty(max(n_row,n_col)), stat=info) + if (info /= 0) then + call psb_errpush(4010,name,a_err='Allocate') + goto 9999 + end if + + mlprec_wrk(ilev)%x2l(:) = zzero + mlprec_wrk(ilev)%y2l(:) = zzero + mlprec_wrk(ilev)%tx(1:n_row) = mlprec_wrk(ilev-1)%x2l(1:n_row) + mlprec_wrk(ilev)%tx(n_row+1:max(n_row,n_col)) = zzero + mlprec_wrk(ilev)%ty(:) = zzero + + ismth=baseprecv(ilev)%iprcparm(smth_kind_) + + if (ismth /= no_smth_) then + ! + ! Smoothed aggregation + ! + + + if (baseprecv(ilev)%iprcparm(glb_smth_) >0) then + call psb_halo(mlprec_wrk(ilev-1)%x2l,baseprecv(ilev-1)%base_desc,& + & info,work=work) + if(info /=0) goto 9999 + else + mlprec_wrk(ilev-1)%x2l(n_row+1:max(n_row,n_col)) = zzero + end if + + call psb_csmm(zone,baseprecv(ilev)%av(sm_pr_t_),mlprec_wrk(ilev-1)%x2l,& + & zzero,mlprec_wrk(ilev)%x2l,info) + if(info /=0) goto 9999 + + else + ! + ! Raw aggregation, may take shortcut + ! + do i=1,n_row + mlprec_wrk(ilev)%x2l(baseprecv(ilev)%mlia(i)) = & + & mlprec_wrk(ilev)%x2l(baseprecv(ilev)%mlia(i)) + & + & mlprec_wrk(ilev-1)%x2l(i) + end do + + end if + + if (baseprecv(ilev)%iprcparm(coarse_mat_)==mat_repl_) Then + call psb_sum(ictxt,mlprec_wrk(ilev)%x2l(1:nrg)) + else if (baseprecv(ilev)%iprcparm(coarse_mat_) /= mat_distr_) Then + write(0,*) 'Unknown value for baseprecv(2)%iprcparm(coarse_mat_) ',& + & baseprecv(ilev)%iprcparm(coarse_mat_) + endif + + call psb_baseprc_aply(zone,baseprecv(ilev),& + & mlprec_wrk(ilev)%x2l,zzero,mlprec_wrk(ilev)%y2l,& + & baseprecv(ilev)%desc_data, 'N',work,info) + + enddo + + do ilev =nlev,2,-1 + + ismth=baseprecv(ilev)%iprcparm(smth_kind_) + n_row = baseprecv(ilev-1)%base_desc%matrix_data(psb_n_row_) + n_col = baseprecv(ilev-1)%desc_data%matrix_data(psb_n_col_) + nr2l = baseprecv(ilev)%desc_data%matrix_data(psb_n_col_) + nrg = baseprecv(ilev)%desc_data%matrix_data(psb_n_row_) + + if (ismth /= no_smth_) then + + call psb_csmm(zone,baseprecv(ilev)%av(sm_pr_),mlprec_wrk(ilev)%y2l,& + & zone,mlprec_wrk(ilev-1)%y2l,info) + if(info /=0) goto 9999 + + else + + do i=1, n_row + mlprec_wrk(ilev-1)%y2l(i) = mlprec_wrk(ilev-1)%y2l(i) + & + & mlprec_wrk(ilev)%y2l(baseprecv(ilev)%mlia(i)) + enddo + + end if + end do + + call psb_geaxpby(alpha,mlprec_wrk(1)%y2l,zone,y,baseprecv(1)%base_desc,info) + if(info /=0) goto 9999 + + + case(mult_ml_prec_) + + ! + ! Multiplicative multilevel + ! Pre/post smoothing versions. + ! + + select case(baseprecv(2)%iprcparm(smth_pos_)) + + case(post_smooth_) + + + ! + ! Post smoothing. + ! 1. X(1) = Xext + ! 2. DO ILEV=2, NLEV :: X(ILEV) = AV(PR_SM_T_,ILEV)*X(ILEV-1) + ! 3. Y(NLEV) = (K(NLEV)**(-1))*X(NLEV) + ! 4. DO ILEV=NLEV-1,1,-1 + ! Y(ILEV) = AV(PR_SM_,ILEV+1)*Y(ILEV+1) + ! Y(ILEV) = Y(ILEV) + (K(ILEV)**(-1))*(X(ILEV)-A(ILEV)*Y(ILEV)) + ! + ! 5. Yext = beta*Yext + Y(1) + ! + ! Note: level numbering reversed wrt ref. DD, i.e. + ! 1..NLEV <=> (j) <-> 0 + ! + ! Also: post smoothing is not spelled out in detail in DD. + ! + ! + + + n_col = desc_data%matrix_data(psb_n_col_) + nr2l = baseprecv(1)%desc_data%matrix_data(psb_n_col_) + + allocate(mlprec_wrk(1)%x2l(nr2l),mlprec_wrk(1)%y2l(nr2l), & + & mlprec_wrk(1)%tx(nr2l), stat=info) + mlprec_wrk(1)%x2l(:) = zzero + mlprec_wrk(1)%y2l(:) = zzero + mlprec_wrk(1)%tx(:) = zzero + + call psb_geaxpby(zone,x,zzero,mlprec_wrk(1)%tx,& + & baseprecv(1)%base_desc,info) + call psb_geaxpby(zone,x,zzero,mlprec_wrk(1)%x2l,& + & baseprecv(1)%base_desc,info) + + do ilev=2, nlev + n_row = baseprecv(ilev-1)%base_desc%matrix_data(psb_n_row_) + n_col = baseprecv(ilev-1)%desc_data%matrix_data(psb_n_col_) + nr2l = baseprecv(ilev)%desc_data%matrix_data(psb_n_col_) + nrg = baseprecv(ilev)%desc_data%matrix_data(psb_n_row_) + ismth = baseprecv(ilev)%iprcparm(smth_kind_) + + allocate(mlprec_wrk(ilev)%tx(nr2l),mlprec_wrk(ilev)%y2l(nr2l),& + & mlprec_wrk(ilev)%x2l(nr2l), stat=info) + + if (info /= 0) then + call psb_errpush(4010,name,a_err='Allocate') + goto 9999 + end if + + mlprec_wrk(ilev)%x2l(:) = zzero + mlprec_wrk(ilev)%y2l(:) = zzero + mlprec_wrk(ilev)%tx(:) = zzero + if (ismth /= no_smth_) then + ! + ! Smoothed aggregation + ! + if (baseprecv(ilev)%iprcparm(glb_smth_) >0) then + call psb_halo(mlprec_wrk(ilev-1)%x2l,& + & baseprecv(ilev-1)%base_desc,info,work=work) + if(info /=0) goto 9999 + else + mlprec_wrk(ilev-1)%x2l(n_row+1:max(n_row,n_col)) = zzero + end if + + call psb_csmm(zone,baseprecv(ilev)%av(sm_pr_t_),mlprec_wrk(ilev-1)%x2l, & + & zzero,mlprec_wrk(ilev)%x2l,info) + if(info /=0) goto 9999 + + else + ! + ! Raw aggregation, may take shortcut + ! + do i=1,n_row + mlprec_wrk(ilev)%x2l(baseprecv(ilev)%mlia(i)) = & + & mlprec_wrk(ilev)%x2l(baseprecv(ilev)%mlia(i)) + & + & mlprec_wrk(ilev-1)%x2l(i) + end do + end if + + if (baseprecv(ilev)%iprcparm(coarse_mat_)==mat_repl_) Then + call psb_sum(ictxt,mlprec_wrk(ilev)%x2l(1:nrg)) + else if (baseprecv(ilev)%iprcparm(coarse_mat_) /= mat_distr_) Then + write(0,*) 'Unknown value for baseprecv(2)%iprcparm(coarse_mat_) ',& + & baseprecv(ilev)%iprcparm(coarse_mat_) + endif + call psb_geaxpby(zone,mlprec_wrk(ilev)%x2l,zzero,mlprec_wrk(ilev)%tx,& + & baseprecv(ilev)%base_desc,info) + if(info /=0) goto 9999 + + enddo + + + call psb_baseprc_aply(zone,baseprecv(nlev),mlprec_wrk(nlev)%x2l, & + & zzero, mlprec_wrk(nlev)%y2l,baseprecv(nlev)%desc_data,'N',work,info) + + if(info /=0) goto 9999 + + + do ilev=nlev-1, 1, -1 + ismth = baseprecv(ilev+1)%iprcparm(smth_kind_) + if (ismth /= no_smth_) then + if (ismth == smth_omg_) & + & call psb_halo(mlprec_wrk(ilev+1)%y2l,baseprecv(ilev+1)%desc_data,& + & info,work=work) + call psb_csmm(zone,baseprecv(ilev+1)%av(sm_pr_),mlprec_wrk(ilev+1)%y2l,& + & zzero,mlprec_wrk(ilev)%y2l,info) + if(info /=0) goto 9999 + + else + n_row = baseprecv(ilev)%base_desc%matrix_data(psb_n_row_) + mlprec_wrk(ilev)%y2l(:) = zzero + do i=1, n_row + mlprec_wrk(ilev)%y2l(i) = mlprec_wrk(ilev)%y2l(i) + & + & mlprec_wrk(ilev+1)%y2l(baseprecv(ilev+1)%mlia(i)) + enddo + + end if + + call psb_spmm(-zone,baseprecv(ilev)%base_a,mlprec_wrk(ilev)%y2l,& + & zone,mlprec_wrk(ilev)%tx,baseprecv(ilev)%base_desc,info,work=work) + + if(info /=0) goto 9999 + + call psb_baseprc_aply(zone,baseprecv(ilev),mlprec_wrk(ilev)%tx,& + & zone,mlprec_wrk(ilev)%y2l,baseprecv(ilev)%base_desc, trans, work,info) + + if(info /=0) goto 9999 + + enddo + + call psb_geaxpby(alpha,mlprec_wrk(1)%y2l,beta,y,baseprecv(1)%base_desc,info) + + if(info /=0) goto 9999 + + + case(pre_smooth_) + + + ! + ! Pre smoothing. + ! 1. X(1) = Xext + ! 2. Y(1) = (K(1)**(-1))*X(1) + ! 3. TX(1) = X(1) - A(1)*Y(1) + ! 4. DO ILEV=2, NLEV + ! X(ILEV) = AV(PR_SM_T_,ILEV)*TX(ILEV-1) + ! Y(ILEV) = (K(ILEV)**(-1))*X(ILEV) + ! TX(ILEV) = (X(ILEV)-A(ILEV)*Y(ILEV)) + ! 5. DO ILEV=NLEV-1,1,-1 + ! Y(ILEV) = Y(ILEV) + AV(PR_SM_,ILEV+1)*Y(ILEV+1) + ! 6. Yext = beta*Yext + Y(1) + ! + ! Note: level numbering reversed wrt ref. DD, i.e. + ! 1..NLEV <=> (j) <-> 0 + ! + ! + + n_col = desc_data%matrix_data(psb_n_col_) + nr2l = baseprecv(1)%desc_data%matrix_data(psb_n_col_) + + allocate(mlprec_wrk(1)%x2l(nr2l),mlprec_wrk(1)%y2l(nr2l), & + & mlprec_wrk(1)%tx(nr2l), stat=info) + if (info /= 0) then + call psb_errpush(4010,name,a_err='Allocate') + goto 9999 + end if + + mlprec_wrk(1)%y2l(:) = zzero + + + mlprec_wrk(1)%x2l(:) = x + + call psb_baseprc_aply(zone,baseprecv(1),mlprec_wrk(1)%x2l,& + & zzero,mlprec_wrk(1)%y2l,& + & baseprecv(1)%base_desc,& + & trans,work,info) + + if(info /=0) goto 9999 + + mlprec_wrk(1)%tx = mlprec_wrk(1)%x2l + + call psb_spmm(-zone,baseprecv(1)%base_a,mlprec_wrk(1)%y2l,& + & zone,mlprec_wrk(1)%tx,baseprecv(1)%base_desc,info,work=work) + if(info /=0) goto 9999 + + do ilev = 2, nlev + n_row = baseprecv(ilev-1)%base_desc%matrix_data(psb_n_row_) + n_col = baseprecv(ilev-1)%desc_data%matrix_data(psb_n_col_) + nr2l = baseprecv(ilev)%desc_data%matrix_data(psb_n_col_) + nrg = baseprecv(ilev)%desc_data%matrix_data(psb_n_row_) + ismth = baseprecv(ilev)%iprcparm(smth_kind_) + allocate(mlprec_wrk(ilev)%tx(nr2l),mlprec_wrk(ilev)%y2l(nr2l),& + & mlprec_wrk(ilev)%x2l(nr2l), stat=info) + + + if (info /= 0) then + call psb_errpush(4010,name,a_err='Allocate') + goto 9999 + end if + + mlprec_wrk(ilev)%x2l(:) = zzero + mlprec_wrk(ilev)%y2l(:) = zzero + mlprec_wrk(ilev)%tx(:) = zzero + + + if (ismth /= no_smth_) then + ! + !Smoothed Aggregation + ! + if (baseprecv(ilev)%iprcparm(glb_smth_) >0) then + + call psb_halo(mlprec_wrk(ilev-1)%tx,baseprecv(ilev-1)%base_desc,& + & info,work=work) + if(info /=0) goto 9999 + else + mlprec_wrk(ilev-1)%tx(n_row+1:max(n_row,n_col)) = zzero + end if + + call psb_csmm(zone,baseprecv(ilev)%av(sm_pr_t_),mlprec_wrk(ilev-1)%tx,zzero,& + & mlprec_wrk(ilev)%x2l,info) + if(info /=0) goto 9999 + + else + ! + ! Raw aggregation, may take shortcuts + ! + mlprec_wrk(ilev)%x2l = zzero + do i=1,n_row + mlprec_wrk(ilev)%x2l(baseprecv(ilev)%mlia(i)) = & + & mlprec_wrk(ilev)%x2l(baseprecv(ilev)%mlia(i)) + & + & mlprec_wrk(ilev-1)%tx(i) + end do + end if + + if (baseprecv(ilev)%iprcparm(coarse_mat_)==mat_repl_) then + call psb_sum(ictxt,mlprec_wrk(ilev)%x2l(1:nrg)) + else if (baseprecv(ilev)%iprcparm(coarse_mat_) /= mat_distr_) then + write(0,*) 'Unknown value for baseprecv(2)%iprcparm(coarse_mat_) ',& + & baseprecv(ilev)%iprcparm(coarse_mat_) + endif + + + call psb_baseprc_aply(zone,baseprecv(ilev),mlprec_wrk(ilev)%x2l,& + & zzero,mlprec_wrk(ilev)%y2l,baseprecv(ilev)%desc_data, 'N',work,info) + + if(info /=0) goto 9999 + + if(ilev < nlev) then + mlprec_wrk(ilev)%tx = mlprec_wrk(ilev)%x2l + call psb_spmm(-zone,baseprecv(ilev)%base_a,mlprec_wrk(ilev)%y2l,& + & zone,mlprec_wrk(ilev)%tx,baseprecv(ilev)%base_desc,info,work=work) + if(info /=0) goto 9999 + endif + + enddo + + do ilev = nlev-1, 1, -1 + + ismth=baseprecv(ilev+1)%iprcparm(smth_kind_) + + if (ismth /= no_smth_) then + + if (ismth == smth_omg_) & + & call psb_halo(mlprec_wrk(ilev+1)%y2l,& + & baseprecv(ilev+1)%desc_data,info,work=work) + call psb_csmm(zone,baseprecv(ilev+1)%av(sm_pr_),mlprec_wrk(ilev+1)%y2l,& + & zone,mlprec_wrk(ilev)%y2l,info) + + if(info /=0) goto 9999 + + else + + n_row = baseprecv(ilev+1)%base_desc%matrix_data(psb_n_row_) + do i=1, n_row + mlprec_wrk(ilev)%y2l(i) = mlprec_wrk(ilev)%y2l(i) + & + & mlprec_wrk(ilev+1)%y2l(baseprecv(ilev+1)%mlia(i)) + enddo + + end if + + enddo + + call psb_geaxpby(alpha,mlprec_wrk(1)%y2l,beta,y,& + & baseprecv(1)%base_desc,info) + + if(info /=0) goto 9999 + + + + case(smooth_both_) + + ! + ! Symmetrized smoothing. + ! 1. X(1) = Xext + ! 2. Y(1) = (K(1)**(-1))*X(1) + ! 3. TX(1) = X(1) - A(1)*Y(1) + ! 4. DO ILEV=2, NLEV + ! X(ILEV) = AV(PR_SM_T_,ILEV)*TX(ILEV-1) + ! Y(ILEV) = (K(ILEV)**(-1))*X(ILEV) + ! TX(ILEV) = (X(ILEV)-A(ILEV)*Y(ILEV)) + ! 5. DO ILEV=NLEV-1,1,-1 + ! Y(ILEV) = Y(ILEV) + AV(PR_SM_,ILEV+1)*Y(ILEV+1) + ! Y(ILEV) = Y(ILEV) + (K(ILEV)**(-1))*(X(ILEV)-A(ILEV)*Y(ILEV)) + ! 6. Yext = beta*Yext + Y(1) + ! + ! Note: level numbering reversed wrt ref. DD, i.e. + ! 1..NLEV <=> (j) <-> 0 + ! + ! + n_col = desc_data%matrix_data(psb_n_col_) + nr2l = baseprecv(1)%desc_data%matrix_data(psb_n_col_) + + allocate(mlprec_wrk(1)%x2l(nr2l),mlprec_wrk(1)%y2l(nr2l), & + & mlprec_wrk(1)%ty(nr2l), mlprec_wrk(1)%tx(nr2l), stat=info) + + mlprec_wrk(1)%x2l(:) = zzero + mlprec_wrk(1)%y2l(:) = zzero + mlprec_wrk(1)%tx(:) = zzero + mlprec_wrk(1)%ty(:) = zzero + + + if (info /= 0) then + call psb_errpush(4010,name,a_err='Allocate') + goto 9999 + end if + + call psb_geaxpby(zone,x,zzero,mlprec_wrk(1)%x2l,& + & baseprecv(1)%base_desc,info) + call psb_geaxpby(zone,x,zzero,mlprec_wrk(1)%tx,& + & baseprecv(1)%base_desc,info) + + call psb_baseprc_aply(zone,baseprecv(1),mlprec_wrk(1)%x2l,& + & zzero,mlprec_wrk(1)%y2l,& + & baseprecv(1)%base_desc,& + & trans,work,info) + + if(info /=0) goto 9999 + + mlprec_wrk(1)%ty = mlprec_wrk(1)%x2l + + call psb_spmm(-zone,baseprecv(1)%base_a,mlprec_wrk(1)%y2l,& + & zone,mlprec_wrk(1)%ty,baseprecv(1)%base_desc,info,work=work) + if(info /=0) goto 9999 + + do ilev = 2, nlev + n_row = baseprecv(ilev-1)%base_desc%matrix_data(psb_n_row_) + n_col = baseprecv(ilev-1)%desc_data%matrix_data(psb_n_col_) + nr2l = baseprecv(ilev)%desc_data%matrix_data(psb_n_col_) + nrg = baseprecv(ilev)%desc_data%matrix_data(psb_n_row_) + ismth=baseprecv(ilev)%iprcparm(smth_kind_) + allocate(mlprec_wrk(ilev)%ty(nr2l),mlprec_wrk(ilev)%y2l(nr2l),& + & mlprec_wrk(ilev)%x2l(nr2l), stat=info) + + mlprec_wrk(ilev)%x2l(:) = zzero + mlprec_wrk(ilev)%y2l(:) = zzero + mlprec_wrk(ilev)%tx(:) = zzero + mlprec_wrk(ilev)%ty(:) = zzero + + + if (info /= 0) then + call psb_errpush(4010,name,a_err='Allocate') + goto 9999 + end if + + + if (ismth /= no_smth_) then + ! + !Smoothed Aggregation + ! + if (baseprecv(ilev)%iprcparm(glb_smth_) >0) then + + call psb_halo(mlprec_wrk(ilev-1)%ty,baseprecv(ilev-1)%base_desc,& + & info,work=work) + if(info /=0) goto 9999 + else + mlprec_wrk(ilev-1)%ty(n_row+1:max(n_row,n_col)) = zzero + end if + + call psb_csmm(zone,baseprecv(ilev)%av(sm_pr_t_),mlprec_wrk(ilev-1)%ty,zzero,& + & mlprec_wrk(ilev)%x2l,info) + if(info /=0) goto 9999 + + else + ! + ! Raw aggregation, may take shortcuts + ! + mlprec_wrk(ilev)%x2l = zzero + do i=1,n_row + mlprec_wrk(ilev)%x2l(baseprecv(ilev)%mlia(i)) = & + & mlprec_wrk(ilev)%x2l(baseprecv(ilev)%mlia(i)) + & + & mlprec_wrk(ilev-1)%ty(i) + end do + end if + + if (baseprecv(ilev)%iprcparm(coarse_mat_)==mat_repl_) then + call psb_sum(ictxt,mlprec_wrk(ilev)%x2l(1:nrg)) + else if (baseprecv(ilev)%iprcparm(coarse_mat_) /= mat_distr_) then + write(0,*) 'Unknown value for baseprecv(2)%iprcparm(coarse_mat_) ',& + & baseprecv(ilev)%iprcparm(coarse_mat_) + endif + + call psb_geaxpby(zone,mlprec_wrk(ilev)%x2l,zzero,mlprec_wrk(ilev)%tx,& + & baseprecv(ilev)%base_desc,info) + if(info /=0) goto 9999 + + call psb_baseprc_aply(zone,baseprecv(ilev),mlprec_wrk(ilev)%x2l,& + & zzero,mlprec_wrk(ilev)%y2l,baseprecv(ilev)%desc_data, 'N',work,info) + + if(info /=0) goto 9999 + + if(ilev < nlev) then + mlprec_wrk(ilev)%ty = mlprec_wrk(ilev)%x2l + call psb_spmm(-zone,baseprecv(ilev)%base_a,mlprec_wrk(ilev)%y2l,& + & zone,mlprec_wrk(ilev)%ty,baseprecv(ilev)%base_desc,info,work=work) + if(info /=0) goto 9999 + endif + + enddo + + + do ilev=nlev-1, 1, -1 + + ismth=baseprecv(ilev+1)%iprcparm(smth_kind_) + if (ismth /= no_smth_) then + if (ismth == smth_omg_) & + & call psb_halo(mlprec_wrk(ilev+1)%y2l,baseprecv(ilev+1)%desc_data,& + & info,work=work) + call psb_csmm(zone,baseprecv(ilev+1)%av(sm_pr_),mlprec_wrk(ilev+1)%y2l,& + & zone,mlprec_wrk(ilev)%y2l,info) + if(info /=0) goto 9999 + + else + n_row = baseprecv(ilev)%base_desc%matrix_data(psb_n_row_) + do i=1, n_row + mlprec_wrk(ilev)%y2l(i) = mlprec_wrk(ilev)%y2l(i) + & + & mlprec_wrk(ilev+1)%y2l(baseprecv(ilev+1)%mlia(i)) + enddo + + end if + + call psb_spmm(-zone,baseprecv(ilev)%base_a,mlprec_wrk(ilev)%y2l,& + & zone,mlprec_wrk(ilev)%tx,baseprecv(ilev)%base_desc,info,work=work) + + if(info /=0) goto 9999 + + call psb_baseprc_aply(zone,baseprecv(ilev),mlprec_wrk(ilev)%tx,& + & zone,mlprec_wrk(ilev)%y2l,baseprecv(ilev)%base_desc, trans, work,info) + + if(info /=0) goto 9999 + + enddo + + call psb_geaxpby(alpha,mlprec_wrk(1)%y2l,beta,y,& + & baseprecv(1)%base_desc,info) + + if(info /=0) goto 9999 + + + case default + + call psb_errpush(4013,name,a_err='wrong smooth_pos',& + & i_Err=(/baseprecv(2)%iprcparm(smth_pos_),0,0,0,0/)) + goto 9999 + + end select + + case default + call psb_errpush(4013,name,a_err='wrong mltype',& + & i_Err=(/baseprecv(2)%iprcparm(ml_type_),0,0,0,0/)) + goto 9999 + + end select + + + call mlprec_wrk_free(mlprec_wrk) + deallocate(mlprec_wrk) + + call psb_erractionrestore(err_act) + return + +9999 continue + call psb_errpush(info,name) + call psb_erractionrestore(err_act) + if (err_act.eq.act_abort) then + call psb_error() + return + end if + return + +contains + subroutine mlprec_wrk_free(wrk) + type(psb_mlprec_wrk_type) :: wrk(:) + ! This will not be needed when we have allocatables, as + ! it is sufficient to deallocate the container, and + ! the compiler is supposed to recursively deallocate the + ! various components. + integer i + + do i=1, size(wrk) + if (associated(wrk(i)%tx)) deallocate(wrk(i)%tx) + if (associated(wrk(i)%ty)) deallocate(wrk(i)%ty) + if (associated(wrk(i)%x2l)) deallocate(wrk(i)%x2l) + if (associated(wrk(i)%y2l)) deallocate(wrk(i)%y2l) + if (associated(wrk(i)%b2l)) deallocate(wrk(i)%b2l) + if (associated(wrk(i)%tty)) deallocate(wrk(i)%tty) + end do + end subroutine mlprec_wrk_free + +end subroutine psb_zmlprc_aply + diff --git a/tests/old_run.py b/tests/old_run.py new file mode 100644 index 0000000..3c4de1d --- /dev/null +++ b/tests/old_run.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +""" + Pygments unit tests + ~~~~~~~~~~~~~~~~~~ + + Usage:: + + python run.py [testfile ...] + + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import sys, os +import unittest + +from os.path import dirname, basename, join, abspath + +import pygments + +try: + import coverage +except ImportError: + coverage = None + +testdir = abspath(dirname(__file__)) + +failed = [] +total_test_count = 0 +error_test_count = 0 + + +def err(file, what, exc): + print >>sys.stderr, file, 'failed %s:' % what, + print >>sys.stderr, exc + failed.append(file[:-3]) + + +class QuietTestRunner(object): + """Customized test runner for relatively quiet output""" + + def __init__(self, testname, stream=sys.stderr): + self.testname = testname + self.stream = unittest._WritelnDecorator(stream) + + def run(self, test): + global total_test_count + global error_test_count + result = unittest._TextTestResult(self.stream, True, 1) + test(result) + if not result.wasSuccessful(): + self.stream.write(' FAIL:') + result.printErrors() + failed.append(self.testname) + else: + self.stream.write(' ok\n') + total_test_count += result.testsRun + error_test_count += len(result.errors) + len(result.failures) + return result + + +def run_tests(with_coverage=False): + # needed to avoid confusion involving atexit handlers + import logging + + if sys.argv[1:]: + # test only files given on cmdline + files = [entry + '.py' for entry in sys.argv[1:] if entry.startswith('test_')] + else: + files = [entry for entry in os.listdir(testdir) + if (entry.startswith('test_') and entry.endswith('.py'))] + files.sort() + + WIDTH = 85 + + print >>sys.stderr, \ + ('Pygments %s Test Suite running%s, stand by...' % + (pygments.__version__, + with_coverage and " with coverage analysis" or "")).center(WIDTH) + print >>sys.stderr, ('(using Python %s)' % sys.version.split()[0]).center(WIDTH) + print >>sys.stderr, '='*WIDTH + + if with_coverage: + coverage.erase() + coverage.start() + + for testfile in files: + globs = {'__file__': join(testdir, testfile)} + try: + execfile(join(testdir, testfile), globs) + except Exception, exc: + raise + err(testfile, 'execfile', exc) + continue + sys.stderr.write(testfile[:-3] + ': ') + try: + runner = QuietTestRunner(testfile[:-3]) + # make a test suite of all TestCases in the file + tests = [] + for name, thing in globs.iteritems(): + if name.endswith('Test'): + tests.append((name, unittest.makeSuite(thing))) + tests.sort() + suite = unittest.TestSuite() + suite.addTests([x[1] for x in tests]) + runner.run(suite) + except Exception, exc: + err(testfile, 'running test', exc) + + print >>sys.stderr, '='*WIDTH + if failed: + print >>sys.stderr, '%d of %d tests failed.' % \ + (error_test_count, total_test_count) + print >>sys.stderr, 'Tests failed in:', ', '.join(failed) + ret = 1 + else: + if total_test_count == 1: + print >>sys.stderr, '1 test happy.' + else: + print >>sys.stderr, 'All %d tests happy.' % total_test_count + ret = 0 + + if with_coverage: + coverage.stop() + modules = [mod for name, mod in sys.modules.iteritems() + if name.startswith('pygments.') and mod] + coverage.report(modules) + + return ret + + +if __name__ == '__main__': + with_coverage = False + if sys.argv[1:2] == ['-C']: + with_coverage = bool(coverage) + del sys.argv[1] + sys.exit(run_tests(with_coverage)) diff --git a/tests/run.py b/tests/run.py new file mode 100644 index 0000000..97c832d --- /dev/null +++ b/tests/run.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +""" + Pygments unit tests + ~~~~~~~~~~~~~~~~~~ + + Usage:: + + python run.py [testfile ...] + + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import sys, os + +if sys.version_info >= (3,): + # copy test suite over to "build/lib" and convert it + print ('Copying and converting sources to build/lib/test...') + from distutils.util import copydir_run_2to3 + testroot = os.path.dirname(__file__) + newroot = os.path.join(testroot, '..', 'build/lib/test') + copydir_run_2to3(testroot, newroot) + # make nose believe that we run from the converted dir + os.chdir(newroot) +else: + # only find tests in this directory + os.chdir(os.path.dirname(__file__)) + + +try: + import nose +except ImportError: + print ('nose is required to run the Pygments test suite') + sys.exit(1) + +try: + # make sure the current source is first on sys.path + sys.path.insert(0, '..') + import pygments +except ImportError: + print ('Cannot find Pygments to test: %s' % sys.exc_info()[1]) + sys.exit(1) +else: + print ('Pygments %s test suite running (Python %s)...' % + (pygments.__version__, sys.version.split()[0])) + +nose.main() diff --git a/tests/support.py b/tests/support.py new file mode 100644 index 0000000..505c17d --- /dev/null +++ b/tests/support.py @@ -0,0 +1,15 @@ +# coding: utf-8 +""" +Support for Pygments tests +""" + +import os + + +def location(mod_name): + """ + Return the file and directory that the code for *mod_name* is in. + """ + source = mod_name.endswith("pyc") and mod_name[:-1] or mod_name + source = os.path.abspath(source) + return source, os.path.dirname(source) diff --git a/tests/test_basic_api.py b/tests/test_basic_api.py new file mode 100644 index 0000000..e106b19 --- /dev/null +++ b/tests/test_basic_api.py @@ -0,0 +1,230 @@ +# -*- coding: utf-8 -*- +""" + Pygments basic API tests + ~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import unittest +import random + +from pygments import lexers, formatters, filters, format +from pygments.token import _TokenType, Text +from pygments.lexer import RegexLexer +from pygments.formatters.img import FontNotFound +from pygments.util import BytesIO, StringIO, bytes, b + +import support + +TESTFILE, TESTDIR = support.location(__file__) + +test_content = [chr(i) for i in xrange(33, 128)] * 5 +random.shuffle(test_content) +test_content = ''.join(test_content) + '\n' + +class LexersTest(unittest.TestCase): + + def test_import_all(self): + # instantiate every lexer, to see if the token type defs are correct + for x in lexers.LEXERS.keys(): + c = getattr(lexers, x)() + + def test_lexer_classes(self): + a = self.assert_ + ae = self.assertEquals + # test that every lexer class has the correct public API + for lexer in lexers._iter_lexerclasses(): + a(type(lexer.name) is str) + for attr in 'aliases', 'filenames', 'alias_filenames', 'mimetypes': + a(hasattr(lexer, attr)) + a(type(getattr(lexer, attr)) is list, "%s: %s attribute wrong" % + (lexer, attr)) + result = lexer.analyse_text("abc") + a(isinstance(result, float) and 0.0 <= result <= 1.0) + + inst = lexer(opt1="val1", opt2="val2") + if issubclass(lexer, RegexLexer): + if not hasattr(lexer, '_tokens'): + # if there's no "_tokens", the lexer has to be one with + # multiple tokendef variants + a(lexer.token_variants) + for variant in lexer.tokens: + a('root' in lexer.tokens[variant]) + else: + a('root' in lexer._tokens, '%s has no root state' % lexer) + + tokens = list(inst.get_tokens(test_content)) + txt = "" + for token in tokens: + a(isinstance(token, tuple)) + a(isinstance(token[0], _TokenType)) + if isinstance(token[1], str): + print repr(token[1]) + a(isinstance(token[1], unicode)) + txt += token[1] + ae(txt, test_content, "%s lexer roundtrip failed: %r != %r" % + (lexer.name, test_content, txt)) + + def test_get_lexers(self): + a = self.assert_ + ae = self.assertEquals + # test that the lexers functions work + + for func, args in [(lexers.get_lexer_by_name, ("python",)), + (lexers.get_lexer_for_filename, ("test.py",)), + (lexers.get_lexer_for_mimetype, ("text/x-python",)), + (lexers.guess_lexer, ("#!/usr/bin/python -O\nprint",)), + (lexers.guess_lexer_for_filename, ("a.py", "<%= @foo %>")) + ]: + x = func(opt="val", *args) + a(isinstance(x, lexers.PythonLexer)) + ae(x.options["opt"], "val") + + +class FiltersTest(unittest.TestCase): + + def test_basic(self): + filter_args = { + 'whitespace': {'spaces': True, 'tabs': True, 'newlines': True}, + 'highlight': {'names': ['isinstance', 'lexers', 'x']}, + } + for x in filters.FILTERS.keys(): + lx = lexers.PythonLexer() + lx.add_filter(x, **filter_args.get(x, {})) + text = open(TESTFILE, 'rb').read().decode('utf-8') + tokens = list(lx.get_tokens(text)) + roundtext = ''.join([t[1] for t in tokens]) + if x not in ('whitespace', 'keywordcase'): + # these filters change the text + self.assertEquals(roundtext, text, + "lexer roundtrip with %s filter failed" % x) + + def test_raiseonerror(self): + lx = lexers.PythonLexer() + lx.add_filter('raiseonerror', excclass=RuntimeError) + self.assertRaises(RuntimeError, list, lx.get_tokens('$')) + + def test_whitespace(self): + lx = lexers.PythonLexer() + lx.add_filter('whitespace', spaces='%') + text = open(TESTFILE, 'rb').read().decode('utf-8') + lxtext = ''.join([t[1] for t in list(lx.get_tokens(text))]) + self.failIf(' ' in lxtext) + + def test_keywordcase(self): + lx = lexers.PythonLexer() + lx.add_filter('keywordcase', case='capitalize') + text = open(TESTFILE, 'rb').read().decode('utf-8') + lxtext = ''.join([t[1] for t in list(lx.get_tokens(text))]) + self.assert_('Def' in lxtext and 'Class' in lxtext) + + def test_codetag(self): + lx = lexers.PythonLexer() + lx.add_filter('codetagify') + text = u'# BUG: text' + tokens = list(lx.get_tokens(text)) + self.assertEquals('# ', tokens[0][1]) + self.assertEquals('BUG', tokens[1][1]) + + def test_codetag_boundary(self): + # http://dev.pocoo.org/projects/pygments/ticket/368 + lx = lexers.PythonLexer() + lx.add_filter('codetagify') + text = u'# DEBUG: text' + tokens = list(lx.get_tokens(text)) + self.assertEquals('# DEBUG: text', tokens[0][1]) + + +class FormattersTest(unittest.TestCase): + + def test_public_api(self): + a = self.assert_ + ae = self.assertEquals + ts = list(lexers.PythonLexer().get_tokens("def f(): pass")) + out = StringIO() + # test that every formatter class has the correct public API + for formatter, info in formatters.FORMATTERS.iteritems(): + a(len(info) == 4) + a(info[0], "missing formatter name") # name + a(info[1], "missing formatter aliases") # aliases + a(info[3], "missing formatter docstring") # doc + + if formatter.name == 'Raw tokens': + # will not work with Unicode output file + continue + + try: + inst = formatter(opt1="val1") + except (ImportError, FontNotFound): + continue + try: + inst.get_style_defs() + except NotImplementedError: + # may be raised by formatters for which it doesn't make sense + pass + inst.format(ts, out) + + def test_encodings(self): + from pygments.formatters import HtmlFormatter + + # unicode output + fmt = HtmlFormatter() + tokens = [(Text, u"ä")] + out = format(tokens, fmt) + self.assert_(type(out) is unicode) + self.assert_(u"ä" in out) + + # encoding option + fmt = HtmlFormatter(encoding="latin1") + tokens = [(Text, u"ä")] + self.assert_(u"ä".encode("latin1") in format(tokens, fmt)) + + # encoding and outencoding option + fmt = HtmlFormatter(encoding="latin1", outencoding="utf8") + tokens = [(Text, u"ä")] + self.assert_(u"ä".encode("utf8") in format(tokens, fmt)) + + def test_styles(self): + from pygments.formatters import HtmlFormatter + fmt = HtmlFormatter(style="pastie") + + def test_unicode_handling(self): + # test that the formatter supports encoding and Unicode + tokens = list(lexers.PythonLexer(encoding='utf-8'). + get_tokens("def f(): 'ä'")) + for formatter, info in formatters.FORMATTERS.iteritems(): + try: + inst = formatter(encoding=None) + except (ImportError, FontNotFound): + # some dependency or font not installed + continue + + if formatter.name != 'Raw tokens': + out = format(tokens, inst) + if formatter.unicodeoutput: + self.assert_(type(out) is unicode) + + inst = formatter(encoding='utf-8') + out = format(tokens, inst) + self.assert_(type(out) is bytes, '%s: %r' % (formatter, out)) + # Cannot test for encoding, since formatters may have to escape + # non-ASCII characters. + else: + inst = formatter() + out = format(tokens, inst) + self.assert_(type(out) is bytes, '%s: %r' % (formatter, out)) + + def test_get_formatters(self): + a = self.assert_ + ae = self.assertEquals + # test that the formatters functions work + x = formatters.get_formatter_by_name("html", opt="val") + a(isinstance(x, formatters.HtmlFormatter)) + ae(x.options["opt"], "val") + + x = formatters.get_formatter_for_filename("a.html", opt="val") + a(isinstance(x, formatters.HtmlFormatter)) + ae(x.options["opt"], "val") diff --git a/tests/test_clexer.py b/tests/test_clexer.py new file mode 100644 index 0000000..79b8486 --- /dev/null +++ b/tests/test_clexer.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +""" + Basic CLexer Test + ~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import unittest +import os + +from pygments.token import Text, Number +from pygments.lexers import CLexer + + +class CLexerTest(unittest.TestCase): + + def setUp(self): + self.lexer = CLexer() + + def testNumbers(self): + code = '42 23.42 23. .42 023 0xdeadbeef 23e+42 42e-23' + wanted = [] + for item in zip([Number.Integer, Number.Float, Number.Float, + Number.Float, Number.Oct, Number.Hex, + Number.Float, Number.Float], code.split()): + wanted.append(item) + wanted.append((Text, ' ')) + wanted = [(Text, '')] + wanted[:-1] + [(Text, '\n')] + self.assertEqual(list(self.lexer.get_tokens(code)), wanted) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py new file mode 100644 index 0000000..b308825 --- /dev/null +++ b/tests/test_cmdline.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +""" + Command line test + ~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +# Test the command line interface + +import sys, os +import unittest +import StringIO + +from pygments import highlight +from pygments.cmdline import main as cmdline_main + +import support + +TESTFILE, TESTDIR = support.location(__file__) + + +def run_cmdline(*args): + saved_stdout = sys.stdout + saved_stderr = sys.stderr + new_stdout = sys.stdout = StringIO.StringIO() + new_stderr = sys.stderr = StringIO.StringIO() + try: + ret = cmdline_main(["pygmentize"] + list(args)) + finally: + sys.stdout = saved_stdout + sys.stderr = saved_stderr + return (ret, new_stdout.getvalue(), new_stderr.getvalue()) + + +class CmdLineTest(unittest.TestCase): + + def test_L_opt(self): + c, o, e = run_cmdline("-L") + self.assertEquals(c, 0) + self.assert_("Lexers" in o and "Formatters" in o and + "Filters" in o and "Styles" in o) + c, o, e = run_cmdline("-L", "lexer") + self.assertEquals(c, 0) + self.assert_("Lexers" in o and "Formatters" not in o) + c, o, e = run_cmdline("-L", "lexers") + self.assertEquals(c, 0) + + def test_O_opt(self): + filename = TESTFILE + c, o, e = run_cmdline("-Ofull=1,linenos=true,foo=bar", + "-fhtml", filename) + self.assertEquals(c, 0) + self.assert_("foo, bar=baz=," in o) + + def test_F_opt(self): + filename = TESTFILE + c, o, e = run_cmdline("-Fhighlight:tokentype=Name.Blubb," + "names=TESTFILE filename", + "-fhtml", filename) + self.assertEquals(c, 0) + self.assert_('_filename ' + 'for overriding, thus no lexer found.' + % fn) + try: + name, rest = fn.split("_", 1) + lx = get_lexer_by_name(name) + except ClassNotFound: + raise AssertionError('no lexer found for file %r' % fn) + yield check_lexer, lx, absfn + +def check_lexer(lx, absfn): + text = open(absfn, 'rb').read() + text = text.replace(b('\r\n'), b('\n')) + text = text.strip(b('\n')) + b('\n') + try: + text = text.decode('utf-8') + except UnicodeError: + text = text.decode('latin1') + ntext = [] + for type, val in lx.get_tokens(text): + ntext.append(val) + assert type != Error, 'lexer %s generated error token for %s' % \ + (lx, absfn) + if u''.join(ntext) != text: + raise AssertionError('round trip failed for ' + absfn) diff --git a/tests/test_html_formatter.py b/tests/test_html_formatter.py new file mode 100644 index 0000000..365cc8f --- /dev/null +++ b/tests/test_html_formatter.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +""" + Pygments HTML formatter tests + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import re +import unittest +import StringIO +import tempfile +from os.path import join, dirname, isfile, abspath + +from pygments.lexers import PythonLexer +from pygments.formatters import HtmlFormatter, NullFormatter +from pygments.formatters.html import escape_html + +import support + +TESTFILE, TESTDIR = support.location(__file__) + +tokensource = list(PythonLexer(encoding='utf-8').get_tokens(open(TESTFILE).read())) + + +class HtmlFormatterTest(unittest.TestCase): + def test_correct_output(self): + hfmt = HtmlFormatter(nowrap=True) + houtfile = StringIO.StringIO() + hfmt.format(tokensource, houtfile) + + nfmt = NullFormatter() + noutfile = StringIO.StringIO() + nfmt.format(tokensource, noutfile) + + stripped_html = re.sub('<.*?>', '', houtfile.getvalue()) + escaped_text = escape_html(noutfile.getvalue()) + self.assertEquals(stripped_html, escaped_text) + + def test_external_css(self): + # test correct behavior + # CSS should be in /tmp directory + fmt1 = HtmlFormatter(full=True, cssfile='fmt1.css', outencoding='utf-8') + # CSS should be in TESTDIR (TESTDIR is absolute) + fmt2 = HtmlFormatter(full=True, cssfile=join(TESTDIR, 'fmt2.css'), + outencoding='utf-8') + tfile = tempfile.NamedTemporaryFile(suffix='.html') + fmt1.format(tokensource, tfile) + try: + fmt2.format(tokensource, tfile) + self.assert_(isfile(join(TESTDIR, 'fmt2.css'))) + except IOError: + # test directory not writable + pass + tfile.close() + + self.assert_(isfile(join(dirname(tfile.name), 'fmt1.css'))) + os.unlink(join(dirname(tfile.name), 'fmt1.css')) + try: + os.unlink(join(TESTDIR, 'fmt2.css')) + except OSError: + pass + + def test_all_options(self): + for optdict in [dict(nowrap=True), + dict(linenos=True), + dict(linenos=True, full=True), + dict(linenos=True, full=True, noclasses=True)]: + + outfile = StringIO.StringIO() + fmt = HtmlFormatter(**optdict) + fmt.format(tokensource, outfile) + + def test_valid_output(self): + # test all available wrappers + fmt = HtmlFormatter(full=True, linenos=True, noclasses=True, + outencoding='utf-8') + + handle, pathname = tempfile.mkstemp('.html') + tfile = os.fdopen(handle, 'w+b') + fmt.format(tokensource, tfile) + tfile.close() + catname = os.path.join(TESTDIR, 'dtds', 'HTML4.soc') + try: + try: + import subprocess + ret = subprocess.Popen(['nsgmls', '-s', '-c', catname, pathname], + stdout=subprocess.PIPE).wait() + except ImportError: + # Python 2.3 - no subprocess module + ret = os.popen('nsgmls -s -c "%s" "%s"' % (catname, pathname)).close() + if ret == 32512: raise OSError # not found + except OSError: + # nsgmls not available + pass + else: + self.failIf(ret, 'nsgmls run reported errors') + + os.unlink(pathname) + + def test_get_style_defs(self): + fmt = HtmlFormatter() + sd = fmt.get_style_defs() + self.assert_(sd.startswith('.')) + + fmt = HtmlFormatter(cssclass='foo') + sd = fmt.get_style_defs() + self.assert_(sd.startswith('.foo')) + sd = fmt.get_style_defs('.bar') + self.assert_(sd.startswith('.bar')) + sd = fmt.get_style_defs(['.bar', '.baz']) + fl = sd.splitlines()[0] + self.assert_('.bar' in fl and '.baz' in fl) + + def test_unicode_options(self): + fmt = HtmlFormatter(title=u'Föö', + cssclass=u'bär', + cssstyles=u'div:before { content: \'bäz\' }', + encoding='utf-8') + handle, pathname = tempfile.mkstemp('.html') + tfile = os.fdopen(handle, 'w+b') + fmt.format(tokensource, tfile) + tfile.close() diff --git a/tests/test_latex_formatter.py b/tests/test_latex_formatter.py new file mode 100644 index 0000000..54c4381 --- /dev/null +++ b/tests/test_latex_formatter.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" + Pygments LaTeX formatter tests + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import unittest +import tempfile + +from pygments.formatters import LatexFormatter +from pygments.lexers import PythonLexer + +import support + +TESTFILE, TESTDIR = support.location(__file__) + + +class LatexFormatterTest(unittest.TestCase): + + def test_valid_output(self): + tokensource = list(PythonLexer().get_tokens(open(TESTFILE).read())) + fmt = LatexFormatter(full=True, encoding='latin1') + + handle, pathname = tempfile.mkstemp('.tex') + # place all output files in /tmp too + old_wd = os.getcwd() + os.chdir(os.path.dirname(pathname)) + tfile = os.fdopen(handle, 'wb') + fmt.format(tokensource, tfile) + tfile.close() + try: + try: + import subprocess + ret = subprocess.Popen(['latex', '-interaction=nonstopmode', + pathname], + stdout=subprocess.PIPE).wait() + except ImportError: + # Python 2.3 - no subprocess module + ret = os.popen('latex -interaction=nonstopmode "%s"' + % pathname).close() + if ret == 32512: raise OSError # not found + except OSError: + # latex not available + pass + else: + self.failIf(ret, 'latex run reported errors') + + os.unlink(pathname) + os.chdir(old_wd) diff --git a/tests/test_regexlexer.py b/tests/test_regexlexer.py new file mode 100644 index 0000000..5928459 --- /dev/null +++ b/tests/test_regexlexer.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +""" + Pygments regex lexer tests + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import unittest + +from pygments.token import Text +from pygments.lexer import RegexLexer + + +class TestLexer(RegexLexer): + """Test tuple state transitions including #pop.""" + tokens = { + 'root': [ + ('a', Text.Root, 'rag'), + ('e', Text.Root), + ], + 'beer': [ + ('d', Text.Beer, ('#pop', '#pop')), + ], + 'rag': [ + ('b', Text.Rag, '#push'), + ('c', Text.Rag, ('#pop', 'beer')), + ], + } + + +class TupleTransTest(unittest.TestCase): + def test(self): + lx = TestLexer() + toks = list(lx.get_tokens_unprocessed('abcde')) + self.assertEquals(toks, + [(0, Text.Root, 'a'), (1, Text.Rag, 'b'), (2, Text.Rag, 'c'), + (3, Text.Beer, 'd'), (4, Text.Root, 'e')]) diff --git a/tests/test_token.py b/tests/test_token.py new file mode 100644 index 0000000..96ac75c --- /dev/null +++ b/tests/test_token.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" + Test suite for the token module + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import unittest +import StringIO +import sys + +from pygments import token + + +class TokenTest(unittest.TestCase): + + def test_tokentype(self): + e = self.assertEquals + r = self.assertRaises + + t = token.String + + e(t.split(), [token.Token, token.Literal, token.String]) + + e(t.__class__, token._TokenType) + + def test_functions(self): + self.assert_(token.is_token_subtype(token.String, token.String)) + self.assert_(token.is_token_subtype(token.String, token.Literal)) + self.failIf(token.is_token_subtype(token.Literal, token.String)) + + self.assert_(token.string_to_tokentype(token.String) is token.String) + self.assert_(token.string_to_tokentype('') is token.Token) + self.assert_(token.string_to_tokentype('String') is token.String) + + def test_sanity_check(self): + stp = token.STANDARD_TYPES.copy() + stp[token.Token] = '---' # Token and Text do conflict, that is okay + t = {} + for k, v in stp.iteritems(): + t.setdefault(v, []).append(k) + if len(t) == len(stp): + return # Okay + + for k, v in t.iteritems(): + if len(v) > 1: + self.fail("%r has more than one key: %r" % (k, v)) diff --git a/tests/test_using_api.py b/tests/test_using_api.py new file mode 100644 index 0000000..ad8f641 --- /dev/null +++ b/tests/test_using_api.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +""" + Pygments tests for using() + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import unittest + +from pygments.lexer import using, bygroups, this, RegexLexer +from pygments.token import String, Text, Keyword + +class TestLexer(RegexLexer): + tokens = { + 'root': [ + (r'#.*', + using(this, state='invalid')), + (r'(")(.+?)(")', + bygroups(String, using(this, state='string'), String)), + (r'[^"]+', Text), + ], + 'string': [ + (r'.+', Keyword), + ], + } + + +class UsingStateTest(unittest.TestCase): + def test_basic(self): + expected = [(Text, 'a'), (String, '"'), (Keyword, 'bcd'), + (String, '"'), (Text, 'e\n')] + t = list(TestLexer().get_tokens('a"bcd"e')) + self.assertEquals(t, expected) + + def test_error(self): + def gen(): + return list(TestLexer().get_tokens('#a')) + self.assertRaises(KeyError, gen) diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 0000000..4dcbcd0 --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +""" + Test suite for the util module + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import unittest +import os + +from pygments import util + + +class UtilTest(unittest.TestCase): + + def test_getoptions(self): + raises = self.assertRaises + equals = self.assertEquals + + equals(util.get_bool_opt({}, 'a', True), True) + equals(util.get_bool_opt({}, 'a', 1), True) + equals(util.get_bool_opt({}, 'a', 'true'), True) + equals(util.get_bool_opt({}, 'a', 'no'), False) + raises(util.OptionError, util.get_bool_opt, {}, 'a', []) + raises(util.OptionError, util.get_bool_opt, {}, 'a', 'foo') + + equals(util.get_int_opt({}, 'a', 1), 1) + raises(util.OptionError, util.get_int_opt, {}, 'a', []) + raises(util.OptionError, util.get_int_opt, {}, 'a', 'bar') + + equals(util.get_list_opt({}, 'a', [1]), [1]) + equals(util.get_list_opt({}, 'a', '1 2'), ['1', '2']) + raises(util.OptionError, util.get_list_opt, {}, 'a', 1) + + + def test_docstring_headline(self): + def f1(): + """ + docstring headline + + other text + """ + def f2(): + """ + docstring + headline + + other text + """ + + self.assertEquals(util.docstring_headline(f1), "docstring headline") + self.assertEquals(util.docstring_headline(f2), "docstring headline") + + def test_analysator(self): + class X(object): + def analyse(text): + return 0.5 + analyse = util.make_analysator(analyse) + self.assertEquals(X.analyse(''), 0.5) + + def test_shebang_matches(self): + self.assert_(util.shebang_matches('#!/usr/bin/env python', r'python(2\.\d)?')) + self.assert_(util.shebang_matches('#!/usr/bin/python2.4', r'python(2\.\d)?')) + self.assert_(util.shebang_matches('#!/usr/bin/startsomethingwith python', + r'python(2\.\d)?')) + self.assert_(util.shebang_matches('#!C:\\Python2.4\\Python.exe', + r'python(2\.\d)?')) + + self.failIf(util.shebang_matches('#!/usr/bin/python-ruby', r'python(2\.\d)?')) + self.failIf(util.shebang_matches('#!/usr/bin/python/ruby', r'python(2\.\d)?')) + self.failIf(util.shebang_matches('#!', r'python')) + + def test_doctype_matches(self): + self.assert_(util.doctype_matches(' ', + 'html.*')) + self.failIf(util.doctype_matches(' ', + 'html.*')) + self.assert_(util.html_doctype_matches( + '')) + + def test_xml(self): + self.assert_(util.looks_like_xml( + '')) + self.assert_(util.looks_like_xml('abc')) + self.failIf(util.looks_like_xml('')) -- 2.7.4