pax_global_header00006660000000000000000000000064136644044610014522gustar00rootroot0000000000000052 comment=d30855fe96e33fed39bd5aa7ba6879ba62306860 coderay-1.1.3/000077500000000000000000000000001366440446100131525ustar00rootroot00000000000000coderay-1.1.3/.codeclimate.yml000066400000000000000000000007071366440446100162300ustar00rootroot00000000000000version: "2" checks: argument-count: enabled: false complex-logic: enabled: false file-lines: enabled: false identical-code: enabled: false method-complexity: enabled: false method-count: enabled: false method-lines: enabled: false nested-control-flow: enabled: false return-statements: enabled: false similar-code: enabled: false plugins: rubocop: enabled: true channel: rubocop-0-76 coderay-1.1.3/.gitignore000066400000000000000000000002461366440446100151440ustar00rootroot00000000000000.* bench/example.* coverage doc Gemfile.lock old-stuff pkg spec/examples.txt spec/reports test/executable/source.rb.html test/executable/source.rb.json test/scanners coderay-1.1.3/.rubocop.yml000066400000000000000000000010541366440446100154240ustar00rootroot00000000000000inherit_from: .rubocop_todo.yml AllCops: TargetRubyVersion: 2.3 DisplayStyleGuide: true Exclude: - 'test/scanners/**/*' - 'bench/example.ruby' - 'old-stuff/**/*' - 'test/lib/**/*' Gemspec/RequiredRubyVersion: Enabled: false Gemspec/DuplicatedAssignment: Enabled: false Layout/AccessModifierIndentation: Enabled: false Layout/AlignArguments: Enabled: false Layout/AlignArray: Enabled: false Layout/AlignHash: Enabled: false Layout/SpaceInsideBlockBraces: EnforcedStyle: space EnforcedStyleForEmptyBraces: space coderay-1.1.3/.rubocop_todo.yml000066400000000000000000001004421366440446100164520ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2019-11-24 03:18:41 +0100 using RuboCop version 0.76.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 26 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IndentOneStep, IndentationWidth. # SupportedStyles: case, end Layout/CaseIndentation: Exclude: - 'lib/coderay/scanners/css.rb' - 'lib/coderay/scanners/sass.rb' - 'lib/coderay/scanners/yaml.rb' # Offense count: 4 # Cop supports --auto-correct. Layout/CommentIndentation: Exclude: - 'bin/coderay' - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/scanners/lua.rb' # Offense count: 82 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: leading, trailing Layout/DotPosition: Enabled: false # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: AllowBorderComment, AllowMarginComment. Layout/EmptyComment: Exclude: - 'ideosyncratic-ruby.rb' # Offense count: 30 # Cop supports --auto-correct. Layout/EmptyLineAfterGuardClause: Exclude: - 'lib/coderay/encoders/debug_lint.rb' - 'lib/coderay/encoders/encoder.rb' - 'lib/coderay/encoders/html/css.rb' - 'lib/coderay/encoders/html/numbering.rb' - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/encoders/lint.rb' - 'lib/coderay/encoders/xml.rb' - 'lib/coderay/helpers/file_type.rb' - 'lib/coderay/helpers/plugin_host.rb' - 'lib/coderay/scanners/diff.rb' - 'lib/coderay/scanners/scanner.rb' - 'lib/coderay/scanners/yaml.rb' - 'lib/coderay/tokens.rb' - 'rake_tasks/generator.rake' - 'rake_tasks/test.rake' # Offense count: 6 # Cop supports --auto-correct. Layout/EmptyLineAfterMagicComment: Exclude: - 'lib/coderay.rb' - 'lib/coderay/scanners/clojure.rb' - 'lib/coderay/scanners/php.rb' - 'lib/coderay/scanners/ruby/patterns.rb' - 'lib/coderay/scanners/ruby/string_state.rb' - 'test/functional/basic.rb' # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: AllowAdjacentOneLineDefs, NumberOfEmptyLines. Layout/EmptyLineBetweenDefs: Exclude: - 'lib/coderay/for_redcloth.rb' - 'lib/coderay/tokens.rb' # Offense count: 9 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: around, only_before Layout/EmptyLinesAroundAccessModifier: Exclude: - 'lib/coderay/encoders/filter.rb' - 'lib/coderay/encoders/json.rb' - 'lib/coderay/encoders/text.rb' - 'lib/coderay/encoders/token_kind_filter.rb' - 'lib/coderay/encoders/xml.rb' - 'lib/coderay/encoders/yaml.rb' # Offense count: 18 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only Layout/EmptyLinesAroundClassBody: Exclude: - 'lib/coderay/duo.rb' - 'lib/coderay/encoders/html/css.rb' - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/scanners/c.rb' - 'lib/coderay/scanners/cpp.rb' - 'lib/coderay/scanners/delphi.rb' - 'lib/coderay/scanners/java.rb' - 'lib/coderay/scanners/xml.rb' - 'rake_tasks/code_statistics.rb' # Offense count: 3 # Cop supports --auto-correct. Layout/EmptyLinesAroundMethodBody: Exclude: - 'lib/coderay/scanners/c.rb' - 'lib/coderay/scanners/cpp.rb' - 'lib/coderay/scanners/java.rb' # Offense count: 16 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines Layout/EmptyLinesAroundModuleBody: Exclude: - 'lib/coderay/encoders/html/css.rb' - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/scanners/c.rb' - 'lib/coderay/scanners/cpp.rb' - 'lib/coderay/scanners/css.rb' - 'lib/coderay/scanners/delphi.rb' - 'lib/coderay/scanners/java.rb' - 'lib/coderay/scanners/lua.rb' - 'lib/coderay/scanners/xml.rb' - 'lib/coderay/styles.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleAlignWith, AutoCorrect, Severity. # SupportedStylesAlignWith: keyword, variable, start_of_line Layout/EndAlignment: Exclude: - 'lib/coderay/scanners/css.rb' - 'lib/coderay/scanners/sass.rb' - 'lib/coderay/scanners/yaml.rb' # Offense count: 2 # Configuration parameters: EnforcedStyle. # SupportedStyles: native, lf, crlf Layout/EndOfLine: Exclude: - 'rake_tasks/documentation.rake' - 'rake_tasks/statistic.rake' # Offense count: 140 # Cop supports --auto-correct. # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. Layout/ExtraSpacing: Enabled: false # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_brackets Layout/IndentFirstArrayElement: EnforcedStyle: consistent # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_braces Layout/IndentFirstHashElement: EnforcedStyle: consistent # Offense count: 48 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: squiggly, active_support, powerpack, unindent Layout/IndentHeredoc: Enabled: false # Offense count: 53 # Cop supports --auto-correct. # Configuration parameters: Width, IgnoredPatterns. Layout/IndentationWidth: Enabled: false # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: AllowDoxygenCommentStyle. Layout/LeadingCommentSpace: Exclude: - 'lib/coderay/scanners/html.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: symmetrical, new_line, same_line Layout/MultilineArrayBraceLayout: Exclude: - 'lib/coderay/scanners/lua.rb' # Offense count: 78 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: aligned, indented, indented_relative_to_receiver Layout/MultilineMethodCallIndentation: Enabled: false # Offense count: 9 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: aligned, indented Layout/MultilineOperationIndentation: Exclude: - 'lib/coderay/encoders/html/numbering.rb' - 'lib/coderay/encoders/token_kind_filter.rb' - 'lib/coderay/for_redcloth.rb' - 'lib/coderay/scanners/groovy.rb' - 'lib/coderay/scanners/php.rb' - 'lib/coderay/scanners/ruby.rb' - 'test/functional/basic.rb' # Offense count: 17 # Cop supports --auto-correct. Layout/SpaceAfterComma: Exclude: - 'ideosyncratic-ruby.rb' - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/helpers/plugin_host.rb' - 'lib/coderay/scanners/css.rb' - 'lib/coderay/scanners/diff.rb' - 'lib/coderay/scanners/ruby.rb' - 'lib/coderay/scanners/ruby/string_state.rb' - 'lib/coderay/scanners/sass.rb' - 'lib/coderay/scanners/yaml.rb' - 'lib/coderay/token_kinds.rb' # Offense count: 37 # Cop supports --auto-correct. # Configuration parameters: AllowForAlignment. Layout/SpaceAroundOperators: AllowForAlignment: true # Offense count: 2 # Cop supports --auto-correct. Layout/SpaceBeforeComment: Exclude: - 'ideosyncratic-ruby.rb' - 'lib/coderay/token_kinds.rb' # Offense count: 1 # Cop supports --auto-correct. Layout/SpaceBeforeSemicolon: Exclude: - 'lib/coderay/scanners/diff.rb' # Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideHashLiteralBraces: Exclude: - 'lib/coderay/encoders/encoder.rb' - 'lib/coderay/scanners/scanner.rb' - 'lib/coderay/styles/style.rb' - 'test/unit/json_encoder.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: space, no_space Layout/SpaceInsideParens: Exclude: - 'rake_tasks/code_statistics.rb' # Offense count: 32 # Cop supports --auto-correct. Layout/SpaceInsidePercentLiteralDelimiters: Exclude: - 'lib/coderay/scanners/clojure.rb' - 'lib/coderay/scanners/groovy.rb' - 'lib/coderay/scanners/java.rb' - 'lib/coderay/scanners/java_script.rb' - 'lib/coderay/scanners/php.rb' - 'lib/coderay/scanners/ruby/patterns.rb' - 'lib/coderay/scanners/sql.rb' # Offense count: 3 # Cop supports --auto-correct. Layout/SpaceInsideRangeLiteral: Exclude: - 'lib/coderay/encoders/html/css.rb' - 'lib/coderay/encoders/html/numbering.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets. # SupportedStyles: space, no_space # SupportedStylesForEmptyBrackets: space, no_space Layout/SpaceInsideReferenceBrackets: Exclude: - 'lib/coderay/scanners/ruby/string_state.rb' # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: space, no_space Layout/SpaceInsideStringInterpolation: Exclude: - 'ideosyncratic-ruby.rb' - 'lib/coderay/scanners/ruby/string_state.rb' # Offense count: 12 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: final_newline, final_blank_line Layout/TrailingBlankLines: Exclude: - 'lib/coderay/for_redcloth.rb' - 'lib/coderay/scanners/clojure.rb' - 'test/executable/source.rb' - 'test/functional/for_redcloth.rb' - 'test/unit/comment_filter.rb' - 'test/unit/count.rb' - 'test/unit/json_encoder.rb' - 'test/unit/lines_of_code.rb' - 'test/unit/null.rb' - 'test/unit/statistic.rb' - 'test/unit/text.rb' - 'test/unit/tokens.rb' # Offense count: 1680 # Cop supports --auto-correct. # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: Enabled: false # Offense count: 485 # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Enabled: false # Offense count: 2 # Configuration parameters: AllowComments. Lint/HandleExceptions: Exclude: - 'bin/coderay' - 'lib/coderay/scanners/ruby.rb' # Offense count: 2 Lint/IneffectiveAccessModifier: Exclude: - 'lib/coderay/encoders/html.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: runtime_error, standard_error Lint/InheritException: Exclude: - 'lib/coderay/helpers/file_type.rb' - 'lib/coderay/helpers/plugin_host.rb' # Offense count: 3 Lint/LiteralAsCondition: Exclude: - 'ideosyncratic-ruby.rb' - 'lib/coderay/scanners/haml.rb' - 'test/executable/suite.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/LiteralInInterpolation: Exclude: - 'ideosyncratic-ruby.rb' # Offense count: 2 Lint/Loop: Exclude: - 'lib/coderay/helpers/plugin_host.rb' - 'lib/coderay/tokens.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/RedundantSplatExpansion: Exclude: - 'lib/coderay/scanners/ruby/string_state.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/SendWithMixinArgument: Exclude: - 'lib/coderay/for_redcloth.rb' # Offense count: 1 # Configuration parameters: IgnoreImplicitReferences. Lint/ShadowedArgument: Exclude: - 'lib/coderay/for_redcloth.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'lib/coderay/encoders/statistic.rb' - 'lib/coderay/scanners/diff.rb' - 'rake_tasks/code_statistics.rb' # Offense count: 38 # Cop supports --auto-correct. # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. Lint/UnusedMethodArgument: Enabled: false # Offense count: 8 Lint/UselessAssignment: Exclude: - 'lib/coderay/scanners/sql.rb' - 'lib/coderay/scanners/yaml.rb' - 'rake_tasks/code_statistics.rb' - 'test/executable/suite.rb' # Offense count: 7 # Configuration parameters: CheckForMethodsWithNoSideEffects. Lint/Void: Exclude: - 'ideosyncratic-ruby.rb' # Offense count: 49 Metrics/AbcSize: Max: 361 # Offense count: 5 # Configuration parameters: CountComments, ExcludedMethods. # ExcludedMethods: refine Metrics/BlockLength: Max: 71 # Offense count: 183 # Configuration parameters: CountBlocks. Metrics/BlockNesting: Max: 8 # Offense count: 27 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 380 # Offense count: 34 Metrics/CyclomaticComplexity: Max: 148 # Offense count: 63 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: Max: 366 # Offense count: 5 # Configuration parameters: CountComments. Metrics/ModuleLength: Max: 410 # Offense count: 34 Metrics/PerceivedComplexity: Max: 161 # Offense count: 2 Naming/AccessorMethodName: Exclude: - 'lib/coderay/scanners/scanner.rb' # Offense count: 24 Naming/ConstantName: Exclude: - 'lib/coderay/helpers/file_type.rb' - 'lib/coderay/scanners/css.rb' - 'lib/coderay/scanners/java/builtin_types.rb' # Offense count: 1 # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms. # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS Naming/FileName: Exclude: - 'ideosyncratic-ruby.rb' # Offense count: 1 # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional Naming/MemoizedInstanceVariableName: Exclude: - 'lib/coderay/scanners/scanner.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: PreferredName. Naming/RescuedExceptionsVariableName: Exclude: - 'bin/coderay' - 'lib/coderay/helpers/plugin_host.rb' # Offense count: 5 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. # AllowedNames: io, id, to, by, on, in, at, ip, db, os Naming/UncommunicativeMethodParamName: Exclude: - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/scanners/diff.rb' - 'lib/coderay/scanners/scanner.rb' # Offense count: 8 # Configuration parameters: EnforcedStyle. # SupportedStyles: snake_case, camelCase Naming/VariableName: Exclude: - 'lib/coderay/encoders/encoder.rb' - 'lib/coderay/encoders/html.rb' - 'test/functional/basic.rb' # Offense count: 2 # Configuration parameters: EnforcedStyle. # SupportedStyles: snake_case, normalcase, non_integer Naming/VariableNumber: Exclude: - 'test/unit/tokens.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect. Security/JSONLoad: Exclude: - 'test/unit/json_encoder.rb' # Offense count: 1 # Cop supports --auto-correct. Security/YAMLLoad: Exclude: - 'test/unit/duo.rb' # Offense count: 1 # Configuration parameters: EnforcedStyle. # SupportedStyles: inline, group Style/AccessModifierDeclarations: Exclude: - 'lib/coderay/encoders/encoder.rb' # Offense count: 8 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: always, conditionals Style/AndOr: Exclude: - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/scanners/clojure.rb' - 'lib/coderay/scanners/erb.rb' - 'lib/coderay/scanners/lua.rb' - 'lib/coderay/scanners/yaml.rb' # Offense count: 9 # Configuration parameters: AllowedChars. Style/AsciiComments: Exclude: - 'lib/coderay/scanners/lua.rb' # Offense count: 1 # Cop supports --auto-correct. Style/Attr: Exclude: - 'lib/coderay/encoders/html/css.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners. # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object # FunctionalMethods: let, let!, subject, watch # IgnoredMethods: lambda, proc, it Style/BlockDelimiters: Exclude: - 'lib/coderay/scanners/python.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: braces, no_braces, context_dependent Style/BracesAroundHashParameters: Exclude: - 'lib/coderay/scanners/ruby/patterns.rb' # Offense count: 3 Style/CaseEquality: Exclude: - 'bin/coderay' - 'rake_tasks/generator.rake' - 'test/executable/suite.rb' # Offense count: 35 # Cop supports --auto-correct. Style/CharacterLiteral: Exclude: - 'ideosyncratic-ruby.rb' - 'lib/coderay/encoders/html/numbering.rb' - 'lib/coderay/scanners/c.rb' - 'lib/coderay/scanners/cpp.rb' - 'lib/coderay/scanners/css.rb' - 'lib/coderay/scanners/go.rb' - 'lib/coderay/scanners/groovy.rb' - 'lib/coderay/scanners/java_script.rb' - 'lib/coderay/scanners/python.rb' - 'lib/coderay/scanners/ruby.rb' - 'lib/coderay/scanners/sass.rb' - 'lib/coderay/scanners/scanner.rb' - 'lib/coderay/scanners/sql.rb' - 'lib/coderay/scanners/yaml.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Exclude: - 'lib/coderay/helpers/word_list.rb' - 'lib/coderay/scanners/java/builtin_types.rb' - 'lib/coderay/scanners/ruby/patterns.rb' # Offense count: 1 # Cop supports --auto-correct. Style/ClassMethods: Exclude: - 'lib/coderay/encoders/html/css.rb' # Offense count: 2 Style/ClassVars: Exclude: - 'lib/coderay/encoders/encoder.rb' # Offense count: 2 Style/CommentedKeyword: Exclude: - 'lib/coderay/scanners/scanner.rb' # Offense count: 16 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. # SupportedStyles: assign_to_condition, assign_inside_condition Style/ConditionalAssignment: Exclude: - 'bin/coderay' - 'coderay.gemspec' - 'lib/coderay/encoders/html.rb' - 'lib/coderay/encoders/html/numbering.rb' - 'lib/coderay/encoders/xml.rb' - 'lib/coderay/scanners/debug.rb' - 'lib/coderay/scanners/html.rb' - 'lib/coderay/scanners/java_script.rb' - 'lib/coderay/scanners/php.rb' - 'lib/coderay/scanners/raydebug.rb' - 'lib/coderay/scanners/scanner.rb' - 'rake_tasks/code_statistics.rb' - 'test/executable/suite.rb' # Offense count: 21 Style/Documentation: Enabled: false # Offense count: 2 Style/DoubleNegation: Exclude: - 'lib/coderay/scanners/python.rb' - 'lib/coderay/scanners/ruby.rb' # Offense count: 4 # Cop supports --auto-correct. Style/EachWithObject: Exclude: - 'bin/coderay' - 'lib/coderay/helpers/plugin.rb' - 'rake_tasks/code_statistics.rb' # Offense count: 4 # Cop supports --auto-correct. Style/EmptyCaseCondition: Exclude: - 'lib/coderay/encoders/xml.rb' - 'lib/coderay/scanners/yaml.rb' # Offense count: 2 # Cop supports --auto-correct. Style/EmptyLiteral: Exclude: - 'lib/coderay/encoders/html/css.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: compact, expanded Style/EmptyMethod: Exclude: - 'lib/coderay/encoders/encoder.rb' - 'lib/coderay/scanners/scanner.rb' # Offense count: 8 # Cop supports --auto-correct. Style/Encoding: Exclude: - 'lib/coderay.rb' - 'lib/coderay/scanners/clojure.rb' - 'lib/coderay/scanners/lua.rb' - 'lib/coderay/scanners/php.rb' - 'lib/coderay/scanners/ruby/patterns.rb' - 'lib/coderay/scanners/ruby/string_state.rb' - 'lib/coderay/scanners/scanner.rb' - 'test/functional/basic.rb' # Offense count: 12 # Cop supports --auto-correct. Style/ExpandPathArguments: Exclude: - 'bench/bench.rb' - 'coderay.gemspec' - 'lib/coderay.rb' - 'test/executable/suite.rb' - 'test/functional/basic.rb' - 'test/functional/examples.rb' - 'test/functional/for_redcloth.rb' - 'test/functional/suite.rb' - 'test/unit/file_type.rb' - 'test/unit/lines_of_code.rb' - 'test/unit/plugin.rb' # Offense count: 22 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: each, for Style/For: Exclude: - 'Rakefile' - 'lib/coderay/encoders/encoder.rb' - 'lib/coderay/encoders/html/css.rb' - 'lib/coderay/helpers/file_type.rb' - 'lib/coderay/helpers/plugin_host.rb' - 'lib/coderay/scanners/diff.rb' - 'lib/coderay/tokens.rb' - 'rake_tasks/generator.rake' - 'rake_tasks/test.rake' - 'test/functional/basic.rb' - 'test/functional/suite.rb' - 'test/unit/html.rb' - 'test/unit/json_encoder.rb' - 'test/unit/suite.rb' - 'test/unit/token_kind_filter.rb' # Offense count: 62 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: format, sprintf, percent Style/FormatString: Enabled: false # Offense count: 87 # Configuration parameters: EnforcedStyle. # SupportedStyles: annotated, template, unannotated Style/FormatStringToken: Enabled: false # Offense count: 112 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: always, never Style/FrozenStringLiteralComment: Enabled: false # Offense count: 9 # Configuration parameters: AllowedVariables. Style/GlobalVars: Exclude: - 'bin/coderay' - 'lib/coderay.rb' - 'lib/coderay/encoders/html.rb' - 'lib/coderay/scanners/ruby.rb' - 'test/functional/suite.rb' - 'test/unit/suite.rb' # Offense count: 16 # Configuration parameters: MinBodyLength. Style/GuardClause: Exclude: - 'lib/coderay/encoders/html.rb' - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/encoders/terminal.rb' - 'lib/coderay/helpers/plugin_host.rb' - 'lib/coderay/scanners/haml.rb' - 'lib/coderay/scanners/html.rb' - 'lib/coderay/scanners/python.rb' - 'lib/coderay/scanners/scanner.rb' - 'test/executable/suite.rb' - 'test/unit/file_type.rb' # Offense count: 306 # Cop supports --auto-correct. # Configuration parameters: UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys Style/HashSyntax: EnforcedStyle: hash_rockets # Offense count: 4 Style/IdenticalConditionalBranches: Exclude: - 'lib/coderay/scanners/html.rb' - 'lib/coderay/scanners/ruby.rb' # Offense count: 2 # Configuration parameters: AllowIfModifier. Style/IfInsideElse: Exclude: - 'lib/coderay/scanners/css.rb' - 'lib/coderay/scanners/sass.rb' # Offense count: 42 # Cop supports --auto-correct. Style/IfUnlessModifier: Enabled: false # Offense count: 3 Style/IfUnlessModifierOfIfUnless: Exclude: - 'lib/coderay/encoders/text.rb' - 'lib/coderay/scanners/erb.rb' - 'rake_tasks/test.rake' # Offense count: 1 # Cop supports --auto-correct. Style/InfiniteLoop: Exclude: - 'lib/coderay/scanners/haml.rb' # Offense count: 2 # Cop supports --auto-correct. Style/LineEndConcatenation: Exclude: - 'lib/coderay/for_redcloth.rb' - 'test/functional/basic.rb' # Offense count: 221 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline Style/MethodDefParentheses: Enabled: false # Offense count: 1 Style/MethodMissingSuper: Exclude: - 'lib/coderay/tokens_proxy.rb' # Offense count: 2 Style/MissingRespondToMissing: Exclude: - 'lib/coderay/tokens.rb' - 'lib/coderay/tokens_proxy.rb' # Offense count: 1 Style/MultilineBlockChain: Exclude: - 'lib/coderay/helpers/plugin_host.rb' # Offense count: 5 # Cop supports --auto-correct. Style/MultilineIfModifier: Exclude: - 'lib/coderay/encoders/text.rb' - 'lib/coderay/scanners/erb.rb' - 'rake_tasks/documentation.rake' - 'rake_tasks/test.rake' - 'test/functional/for_redcloth.rb' # Offense count: 10 Style/MultilineTernaryOperator: Exclude: - 'lib/coderay/scanners/ruby.rb' # Offense count: 7 Style/MultipleComparison: Exclude: - 'lib/coderay/scanners/groovy.rb' - 'lib/coderay/scanners/html.rb' - 'lib/coderay/scanners/java.rb' - 'lib/coderay/scanners/java_script.rb' - 'lib/coderay/scanners/sass.rb' - 'lib/coderay/scanners/yaml.rb' # Offense count: 247 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: Enabled: false # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: both, prefix, postfix Style/NegatedIf: Exclude: - 'lib/coderay/scanners/diff.rb' - 'lib/coderay/scanners/groovy.rb' # Offense count: 6 Style/NestedTernaryOperator: Exclude: - 'Gemfile' - 'lib/coderay/scanners/php.rb' - 'lib/coderay/scanners/python.rb' - 'lib/coderay/scanners/sass.rb' - 'lib/coderay/scanners/sql.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: predicate, comparison Style/NilComparison: Exclude: - 'lib/coderay/encoders/html/numbering.rb' - 'lib/coderay/scanners/python.rb' # Offense count: 3 # Cop supports --auto-correct. Style/Not: Exclude: - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/scanners/clojure.rb' - 'lib/coderay/scanners/erb.rb' # Offense count: 12 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: - 'spec/**/*' - 'lib/coderay/encoders/html.rb' - 'lib/coderay/encoders/html/numbering.rb' - 'lib/coderay/scanners/diff.rb' - 'lib/coderay/scanners/groovy.rb' - 'lib/coderay/scanners/haml.rb' - 'lib/coderay/scanners/lua.rb' - 'lib/coderay/scanners/ruby.rb' - 'lib/coderay/tokens.rb' # Offense count: 1 # Cop supports --auto-correct. Style/OneLineConditional: Exclude: - 'rake_tasks/code_statistics.rb' # Offense count: 1 # Cop supports --auto-correct. Style/OrAssignment: Exclude: - 'lib/coderay/scanners/groovy.rb' # Offense count: 2 # Cop supports --auto-correct. Style/ParallelAssignment: Exclude: - 'lib/coderay/encoders/statistic.rb' - 'lib/coderay/scanners/ruby.rb' # Offense count: 30 # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Enabled: false # Offense count: 6 # Cop supports --auto-correct. Style/PerlBackrefs: Exclude: - 'bin/coderay' - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/for_redcloth.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: short, verbose Style/PreferredHashMethods: Exclude: - 'lib/coderay/encoders/debug_lint.rb' - 'lib/coderay/encoders/lint.rb' - 'lib/coderay/helpers/plugin_host.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Exclude: - 'lib/coderay/encoders/html/css.rb' - 'lib/coderay/scanners/diff.rb' - 'lib/coderay/scanners/scanner.rb' # Offense count: 3 # Cop supports --auto-correct. Style/RedundantSelf: Exclude: - 'lib/coderay/encoders/html/output.rb' - 'lib/coderay/helpers/plugin_host.rb' - 'lib/coderay/scanners/scanner.rb' # Offense count: 1 # Cop supports --auto-correct. Style/RedundantSort: Exclude: - 'test/unit/plugin.rb' # Offense count: 58 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, AllowInnerSlashes. # SupportedStyles: slashes, percent_r, mixed Style/RegexpLiteral: Enabled: false # Offense count: 4 # Cop supports --auto-correct. Style/RescueModifier: Exclude: - 'rake_tasks/code_statistics.rb' - 'rake_tasks/test.rake' - 'test/functional/for_redcloth.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: implicit, explicit Style/RescueStandardError: Exclude: - 'lib/coderay/scanners/ruby.rb' - 'lib/coderay/scanners/scanner.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, Whitelist. # Whitelist: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - 'bin/coderay' - 'lib/coderay/scanners/ruby.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: AllowAsExpressionSeparator. Style/Semicolon: Exclude: - 'lib/coderay/scanners/diff.rb' - 'lib/coderay/scanners/ruby/string_state.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: AllowIfMethodIsEmpty. Style/SingleLineMethods: Exclude: - 'lib/coderay/tokens.rb' # Offense count: 24 # Cop supports --auto-correct. # Configuration parameters: . # SupportedStyles: use_perl_names, use_english_names Style/SpecialGlobalVars: EnforcedStyle: use_perl_names # Offense count: 1 # Cop supports --auto-correct. Style/StderrPuts: Exclude: - 'lib/coderay/encoders/json.rb' # Offense count: 131 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: Enabled: false # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: single_quotes, double_quotes Style/StringLiteralsInInterpolation: Exclude: - 'rake_tasks/code_statistics.rb' # Offense count: 1 Style/StructInheritance: Exclude: - 'lib/coderay/scanners/ruby/string_state.rb' # Offense count: 37 # Cop supports --auto-correct. # Configuration parameters: MinSize. # SupportedStyles: percent, brackets Style/SymbolArray: EnforcedStyle: brackets # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: IgnoredMethods. # IgnoredMethods: respond_to, define_method Style/SymbolProc: Exclude: - 'bin/coderay' - 'lib/coderay/scanners/scanner.rb' - 'test/unit/plugin.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, AllowSafeAssignment. # SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex Style/TernaryParentheses: Exclude: - 'lib/coderay/scanners/diff.rb' # Offense count: 21 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInArrayLiteral: Exclude: - 'lib/coderay/scanners/c.rb' - 'lib/coderay/scanners/cpp.rb' - 'lib/coderay/scanners/css.rb' - 'lib/coderay/scanners/delphi.rb' - 'lib/coderay/scanners/go.rb' - 'lib/coderay/scanners/html.rb' - 'lib/coderay/scanners/json.rb' - 'lib/coderay/scanners/python.rb' - 'lib/coderay/scanners/scanner.rb' - 'test/unit/json_encoder.rb' # Offense count: 26 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInHashLiteral: Exclude: - 'lib/coderay/encoders/html.rb' - 'lib/coderay/encoders/terminal.rb' - 'lib/coderay/encoders/xml.rb' - 'lib/coderay/for_redcloth.rb' - 'lib/coderay/helpers/file_type.rb' - 'lib/coderay/scanners/diff.rb' - 'lib/coderay/scanners/groovy.rb' - 'lib/coderay/scanners/html.rb' - 'lib/coderay/scanners/java.rb' - 'lib/coderay/scanners/java_script.rb' - 'lib/coderay/scanners/ruby/patterns.rb' - 'lib/coderay/scanners/sql.rb' # Offense count: 3 # Cop supports --auto-correct. Style/VariableInterpolation: Exclude: - 'bin/coderay' - 'ideosyncratic-ruby.rb' # Offense count: 10 # Cop supports --auto-correct. # Configuration parameters: WordRegex. # SupportedStyles: percent, brackets Style/WordArray: EnforcedStyle: percent MinSize: 69 # Offense count: 1 # Cop supports --auto-correct. Style/ZeroLengthPredicate: Exclude: - 'lib/coderay/encoders/html.rb' # Offense count: 813 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https Metrics/LineLength: Max: 266 coderay-1.1.3/.simplecov000066400000000000000000000001161366440446100151520ustar00rootroot00000000000000unless RUBY_VERSION[/^2.3/] SimpleCov.command_name $0 SimpleCov.start end coderay-1.1.3/.travis.yml000066400000000000000000000015141366440446100152640ustar00rootroot00000000000000env: global: - "JRUBY_OPTS=-Xcext.enabled=true" - "CC_TEST_REPORTER_ID=faa393209ff0a104cf37511a9a03510bcee37951971b1ca4ffc2af217851d47e" language: ruby rvm: - 1.8.7 - ree - 1.9.3 - 2.0 - 2.1 - 2.2 - 2.3 - 2.4 - 2.5 - 2.6 - 2.7 - ruby-head - jruby matrix: allow_failures: - rvm: 1.8.7 - rvm: ruby-head - rvm: jruby branches: only: - master before_script: - if (ruby -e "exit RUBY_VERSION.to_f >= 2.3"); then export RUBYOPT="--enable-frozen-string-literal"; fi; echo $RUBYOPT - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - chmod +x ./cc-test-reporter - ./cc-test-reporter before-build script: "rake test" # test:scanners" after_script: - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT sudo: false coderay-1.1.3/CREDITS.textile000066400000000000000000000071711366440446100156550ustar00rootroot00000000000000h1. Credits h3. Special Thanks to * licenser (Heinz N. Gies) for ending my QBasic career, inventing the Coder project and the input/output plugin system. CodeRay would not exist without him. * bovi (Daniel Bovensiepen) for helping me out on various occasions. h3. Thanks to * Caleb Clausen for writing "RubyLexer":http://rubyforge.org/projects/rubylexer and lots of very interesting mail traffic * birkenfeld (Georg Brandl) and mitsuhiku (Arnim Ronacher) for PyKleur, now Pygments. You guys rock! * Jamis Buck for writing "Syntax":http://rubyforge.org/projects/syntax — I got some useful ideas from it. * Doug Kearns and everyone else who worked on ruby.vim - it not only helped me coding CodeRay, but also gave me a wonderful target to reach for the Ruby scanner. * everyone who uses CodeBB on "http://www.rubyforen.de":http://www.rubyforen.de and "http://www.python-forum.de":http://www.python-forum.de * iGEL, magichisoka, manveru, WoNáDo and everyone I forgot from rubyforen.de * Dethix from ruby-mine.de * zickzackw * Dookie (who is no longer with us...) and Leonidas from "http://www.python-forum.de":http://www.python-forum.de * Andreas Schwarz for finding out that CaseIgnoringWordList was not case ignoring! Such things really make you write tests. * closure for the first version of the Scheme scanner. * Stefan Walk for the first version of the JavaScript and PHP scanners. * Josh Goebel for another version of the JavaScript scanner, a SQL and a Diff scanner. * Jonathan Younger for pointing out the licence confusion caused by wrong LICENSE file. * Jeremy Hinegardner for finding the shebang-on-empty-file bug in FileType. * Charles Oliver Nutter and Yehuda Katz for helping me benchmark CodeRay on JRuby. * Andreas Neuhaus for pointing out a markup bug in coderay/for_redcloth. * 0xf30fc7 for the FileType patch concerning Delphi file extensions. * The folks at redmine.org - thank you for using and fixing CodeRay! * Keith Pitt for his SQL scanners * Rob Aldred for the terminal encoder * Trans for pointing out $DEBUG dependencies * Flameeyes for finding that Term::ANSIColor was obsolete * matz and all Ruby gods and gurus * The inventors of: the computer, the internet, the true color display, HTML & CSS, VIM, Ruby, pizza, microwaves, guitars, scouting, programming, anime, manga, coke and green ice tea. Where would we be without all those people? h3. Created using * "Ruby":http://ruby-lang.org/ * Chihiro (my Sony VAIO laptop); Henrietta (my old MacBook); Triella, born Rico (my new MacBook); as well as Seras and Hikari (my PCs) * "RDE":http://homepage2.nifty.com/sakazuki/rde_e.html, "VIM":http://vim.org and "TextMate":http://macromates.com * "Subversion":http://subversion.tigris.org/ * "Redmine":http://redmine.org/ * "Firefox":http://www.mozilla.org/products/firefox/, "Firebug":http://getfirebug.com/, "Safari":http://www.apple.com/safari/, and "Thunderbird":http://www.mozilla.org/products/thunderbird/ * "RubyGems":http://docs.rubygems.org/ and "Rake":http://rake.rubyforge.org/ * "TortoiseSVN":http://tortoisesvn.tigris.org/ using Apache via "XAMPP":http://www.apachefriends.org/en/xampp.html * RDoc (though I'm quite unsatisfied with it) * Microsoft Windows (yes, I confess!) and MacOS X * GNUWin32, MinGW and some other tools to make the shell under windows a bit less useless * Term::"ANSIColor":http://term-ansicolor.rubyforge.org/ * "PLEAC":http://pleac.sourceforge.net/ code examples * Github * "Travis CI":http://travis-ci.org/rubychan/github h3. Free * As you can see, CodeRay was created under heavy use of *free* software. * So CodeRay is also *free*. * If you use CodeRay to create software, think about making this software *free*, too. * Thanks :) coderay-1.1.3/Changes-pre-1.0.textile000066400000000000000000000300441366440446100172030ustar00rootroot00000000000000h1=. CodeRay Version History p=. _This files lists all changes in the CodeRay library since the 0.8.4 release._ {{toc}} h2. Changes in 0.9.8 "banister" [2011-05-01] Fixes for JRuby's 1.9 mode and minor issues. h3. Rake tasks * *REMOVED* obsolete @has_rdoc@ gem specification, fixing a warning. h3. @Scanners::Scanner@ * *NEW* method @#scan_rest@ replaces @scan_until(/\z/)@, which is broken in JRuby 1.6 --1.9 mode. See "#297":http://odd-eyed-code.org/issues/297. h3. @Scanners::CSS@ * *FIXED* LOC counting (should be 0). See "#296":http://odd-eyed-code.org/issues/296. h3. @Scanners::Ruby@ * *FIXED* the @IDENT@ pattern not to use character properties, which are broken in JRuby 1.6 --1.9 mode. See "#297":http://odd-eyed-code.org/issues/297, thanks to banister for reporting! h3. @Scanners::SQL@ * *ADDED* more keywords: @between@, @databases@, @distinct@, @fields@, @full@, @having@, @is@, @prompt@, @tables@. See "#221":http://odd-eyed-code.org/issues/221, thanks to Etienne Massip again. h3. @FileType@ * *NEW* regonizes ColdFusion file type extensions @.cfm@ and @.cfc@ as XML. See "#298":http://odd-eyed-code.org/issues/298, thanks to Emidio Stani. h2. Changes in 0.9.7 "Etienne" [2011-01-14] Fixes a dangerous JavaScript scanner bug, and a testing problem with Ruby 1.9.1. h3. Tests * *FIXED* The functional tests now load the lib directory (instead of the gem) in Ruby 1.9.1. h3. @Scanners::JavaScript@ * *FIXED* @KEY_CHECK_PATTERN@ regexp See "#264":http://odd-eyed-code.org/issues/264, thanks to Etienne Massip! h2. Changes in 0.9.6 "WoNáDo" [2010-11-25] Minor improvements to the Ruby scanner and a fix for Ruby 1.9. h3. @Scanners::Ruby@ * *IMPROVED* handling of new hash syntax (keys are marked as @:key@ now, colon is a separate @:operator@ token, all idents can be used as keys) See "#257":http://code.licenser.net/issues/257, thanks to WoNáDo! * *ADDED* @__ENCODING__@ magic constant (Ruby 1.9) * *FIXED*: Scanner no longer tries to modify the input string on Ruby 1.9. See "#260":http://code.licenser.net/issues/260, thanks to Jan Lelis! h2. Changes in 0.9.5 "Germany.rb" [2010-09-28] Support for Rubinius ("#251":http://odd-eyed-code.org/issues/251), improved mutlibyte handling, Ruby 1.9 syntax, and valid HTML. h3. @Encoders::HTML@ * *FIXED*: Line tokens use @span@ with @display: block@ instead of @div@, which was invalid HTML ("#255":http://odd-eyed-code.org/issues/255). h3. @Scanner::Scanner@ * *IMPROVED* handling of encodings in Ruby 1.9: UTF-8 and Windows-1252 are checked. * *NEW*: Invalid chars will be converted to @?@ in Ruby 1.9. * *FIXED* @string=@ method for Rubinius. See "issue 481":http://github.com/evanphx/rubinius/issues/481 on their site. h3. @Scanners::CSS@ * *FIXED*: Don't use non-ASCII regexps. h3. @Scanners::Diff@ * *FIXED*: Highlight unexpected lines as @:comment@. h3. @Scanners::PHP@ * *FIXED*: Use @ASCII-8BIT@ encoding for now. h3. @Scanners::Ruby@ * *ADDED* support for some Ruby 1.9 syntax ("#254":http://odd-eyed-code.org/issues/254): ** the @->@ lambda shortcut ** new Hash syntax using colons (@{ a: b }@) * *FIXED*: Use @UTF-8@ encoding. * *IMPROVED* unicode support on Ruby 1.8 ("#253":http://odd-eyed-code.org/issues/253). * *FIXED* recognition of non-ASCII identifiers in Ruby 1.9, JRuby, and Rubinius ("#253":http://odd-eyed-code.org/issues/253). * *CHANGED* heredoc recognition to ignore delimiters starting with a digit. This is incorrect, but causes less false positives. h3. @Scanners::SQL@ * *FIXED* scanning of comments; nice catch, Rubinius! ("#252":http://odd-eyed-code.org/issues/252) h2. Changes in 0.9.4 "Ramadan" [2010-08-31] Updated command line interface and minor scanner fixes for the Diff, HTML, and RHTML scanners. h3. @coderay@ executable * *FIXED*: Partly rewritten, simplified, fixed. ("#244":http://odd-eyed-code.org/issues/244) h3. @Scanners::Diff@ * *FIXED* handling of change headers with code on the same line as the @@ marker. ("#247":http://odd-eyed-code.org/issues/242) h3. @Scanners::HTML@ * *FIXED* a missing regexp modifier that slowed down the scanning. ("#245":http://odd-eyed-code.org/issues/245) h3. @Scanners::RHTML@ * *FIXED* highlighting of ERB comment blocks. ("#246":http://odd-eyed-code.org/issues/246) h2. Changes in 0.9.3 "Eyjafjallajökull" [2010-04-18] * *FIXED*: Documentation of Tokens. ("#218":http://odd-eyed-code.org/issues/218) h3. @coderay@ executable * *NEW*: automatic TTY detection (uses @Term@ encoder) * *NEW*: optional 3rd parameter for the filename * *FIXED*: Converted to UNIX format. * *FIXED*: Warn about generated files. * *FIXED*: Ensure line break after the output (especially for LoC counter). h3. @Scanners::JavaScript@ * *FIXED*: Don't keep state of XML scanner between calls for E4X literals. h3. @Scanners::Java@, @Scanners::JSON@ * *FIXED*: Close unfinished strings with the correct token kind. h2. Changes in 0.9.2 "Flameeyes" [2010-03-14] * *NEW* Basic tests and a _Rakefile_ are now included in the Gem. [Flameeyes] A @doc@ task is also included. * *FIXED* Use @$CODERAY_DEBUG@ for debugging instead of @$DEBUG@. [Trans] ("#192":http://odd-eyed-code.org/issues/192) * *REMOVED* @Term::Ansicolor@ was bundled under _lib/_, but not used. [Flameeyes] ("#205":http://odd-eyed-code.org/issues/205) * *WORKAROUND* for Ruby bug "#2745":http://redmine.ruby-lang.org/issues/show/2745 h3. @Encoders::Term@ * *FIXED* strings are closed correctly ("#138":http://odd-eyed-code.org/issues/138) * *FIXED* several token kinds had no associated color ("#139":http://odd-eyed-code.org/issues/139) * *NEW* alias @terminal@ *NOTE:* This encoder will be renamed to @Encoders::Terminal@ in the next release. h3. @Scanners::Debug@ * *FIXED* Don't close tokens that are not open. Send @:error@ token instead. h3. @Scanners::Groovy@ * *FIXED* token kind of closing brackets is @:operator@ instead of @nil@ ("#148":http://odd-eyed-code.org/issues/148) h3. @Scanners::PHP@ * *FIXED* allow @\@ operator (namespace separator) ("#209":http://odd-eyed-code.org/issues/209) h3. @Scanners::YAML@ * *FIXED* doesn't send debug tokens when @$DEBUG@ is true [Trans] ("#149":http://odd-eyed-code.org/issues/149) h2. Changes in 0.9.1 [2009-12-31] h3. Token classes * *NEW* token classes @:complex@, @:decorator@, @:imaginary@ (all for Python) * *REMOVED* token class @:procedure@ – use @:function@ or @:method@ instead. h3. @Tokens@ * *NEW* method @#scanner@ Stores the scanner. * *REMOVED* methods @.write_token@, @.read_token@, @.escape@, @.unescape@ They were only used by the @Tokens@ encoder, which was removed also. h3. @Encoders::Encoder@ * *REMOVED* Don't require the _stringio_ library. * *NEW* public methods @#open_token@, @#close_token@, @#begin_line@, @#end_line@ These methods are called automatically, like @#text_token@. * *NEW* proteced method @#append_encoded_token_to_output@ h3. @Encoders::Tokens@ * *REMOVED* – use @Tokens#dump@ and @Tokens.load@. h3. @Encoders::Filter@ * *NEW* A @Filter@ encoder has another @Tokens@ instance as output. h3. @Encoders::TokenClassFilter@ * *NEW* It takes 2 options, @:exclude@ and @:include@, that specify which token classes to include or exclude for the output. They can be a single token class, an @Array@ of classes, or the value @:all@. h3. @Encoders::CommentFilter@ * *NEW* Removes tokens of the @:comment@ class. h3. @Encoders::LinesOfCode@ * *NEW* Counts the lines of code according to the @KINDS_NOT_LOC@ token class list defined by the scanner. It uses the new @TokenClassFilter@. Alias: @:loc@, as in @tokens.loc@. h3. @Encoders::JSON@ * *NEW* Outputs tokens in a simple JSON format. h3. @Encoders::Term@ * *NEW* (beta, by Rob Aldred) Outputs code highlighted for a color terminal. h3. @Encoders::HTML@ * *NEW* option @:title@ (default value is _CodeRay output_) Setting this changes the title of the HTML page. * *NEW* option @:highlight_lines@ (default: @nil@) Highlights the given set of line numbers. - *REMOVED* option @:level@ It didn't do anything. CodeRay always outputs XHTML. h3. @Encoders::Text@ * Uses @Encoder@ interface with @super@ and @#text_token@. h3. @Encoders::XML@ * @FIXED@ ("#94":http://odd-eyed-code.org/issues/94) It didn't work at all. h3. Scanners * *NEW* Mapped @:h@ to @:c@, @:cplusplus@ and @:'c++'@ to @:cpp@, @:ecma@, @:ecmascript@, @:ecma_script@ to @:java_script@, @:pascal@ to @:delphi@, and @:plain@ to @:plaintext@. h3. @Scanners::Scanner@ * *NEW* constant @KINDS_NOT_LOC@ A list of all token classes not considered in LOC count. Added appropriate values for scanners. * *NEW* method @#lang@ returns the scanner's lang, which is its @plugin_id@. * *FIXED* automatic, safe UTF-8 detection _[Ruby 1.9]_ * *FIXED* column takes care of multibyte encodings _[Ruby 1.9]_ * *FIXED* is dumpable (@Tokens@ store their scanner in an @@scanner@ variable) h3. @Scanners::Cpp@ * *NEW* (C++) h3. @Scanners::Groovy@ * *NEW* (beta) h3. @Scanners::Python@ * *NEW* h3. @Scanners::PHP@ * *NEW* (based on Stefan Walk's work) h3. @Scanners::SQL@ * *NEW* (based on code by Josh Goebel) h3. @Scanners::C@ * *IMPROVED* added a list of @:directive@ tokens that were @:reserved@ before * *IMPROVED* detection of labels * *IMPROVED* allow @1L@ and @1LL@ style literals h3. @Scanners::CSS@ * *IMPROVED* element selectors are highlighted as @:type@ instead of @:keyword@ h3. @Scanners::Delphi@ * *IMPROVED* Don't cache tokens in CaseIgnoringWordList. h3. @Scanners::Java@ * *IMPROVED* @assert@ is highlighted as a @:keyword@ now * *IMPROVED* @const@ and @goto@ are highlighted as @:reserved@ * *IMPROVED* @false@, @true@, and @null@ are highlighted as @:pre_constant@ * *IMPROVED* @threadsafe@ is no longer a @:directive@ * *IMPROVED* @String@ is highlighted as a @:pre_type@ * *IMPROVED* built-in classes ending with _Error_ or _Exception_ are highlighted as a @:exception@ instead of @:pre_type@ h3. @Scanners::JavaScript@ * *NEW* a list of @PREDEFINED_CONSTANTS@ to be highlighted as @:pre_constant@ * *NEW* XML literals are recognized and highlighted * *NEW* function name highlighting * *IMPROVED* @.1@ is highlighted a number * *FIXED* strings close with the correct kind when terminated unexpectedly h3. @Scanners::JSON@ * *IMPROVED* constants (@true@, @false@, @nil@) are highlighted as @:value@ h3. @Scanners::Ruby@ * *IMPROVED* @Patterns::KEYWORDS_EXPECTING_VALUE@ for more accurate @value_expected@ detection * *IMPROVED* handling of @\@ as a string delimiter * *IMPROVED* handling of unicode strings; automatic switching to unicode * *IMPROVED* highlighting of @self.method@ definitions * *REMOVED* @Patterns::FANCY_START_SAVE@ (obsolete) * *FIXED* encoding issues _[Ruby 1.9]_ * *FIXED* a problem in early Ruby 1.8.6 patch versions with @Regexp.escape@ h3. @Scanners::YAML@ * *IMPROVED* indentation detection h3. @Styles::Cycnus@ * changed a few colors (exceptions, inline strings, predefined types) h3. @Plugin@ * *NEW* method @#title@ Set and get the plugin's title. Titles can be arbitrary strings. * *NEW* method @#helper@ loads helpers from different plugins Use this syntax: @helper 'other_plugin/helper_name'@ h3. @FileType@ * *NEW* @FileType[]@ takes @Pathname@ instances * *NEW* regonizes @.cc@, @.cpp@, @.cp@, @.cxx@, @.c++@, @.C@, @.hh@, @.hpp@, @.h++@, @.cu@ extensions (C++) Thanks to Sander Cox and the TextMate C bundle. * *NEW* regonizes @.pas@, @.dpr@ extensions (Delphi) * *NEW* regonizes @.gvy@, @.groovy@ extensions (Groovy) * *NEW* regonizes @.php@, @.php3@, @.php4@, @.php5@ extensions (PHP) * *NEW* regonizes @.py@, @.py3@, @.pyw@ extensions (Python) * *NEW* regonizes @.rxml@ extension (Ruby) * *NEW* regonizes @.sql@ extension (SQL) * File types list was sorted alphabetically. h3. @CaseIgnoringWordList@ * *FIXED* ("#97":http://odd-eyed-code.org/issues/97) The default value is no longer ignored. h3. @ForRedCloth@ * *FIXED* for RedCloth versions 4.2.0+ ("#119":http://odd-eyed-code.org/issues/119) h3. Cleanups * warnings about character classes _[Ruby 1.9]_ * encoding issues _[Ruby 1.9]_ * documentation, code coderay-1.1.3/Changes.textile000066400000000000000000000472611366440446100161340ustar00rootroot00000000000000h1=. CodeRay Version History p=. _This files lists all changes in the CodeRay library since the 0.9.8 release._ h2. Changes in 1.1.3 * Tokens: Ensure Ruby 2.6 compatibility. [#233, thanks to Jun Aruga] * SQL scanner: Add @numeric@ data type. [#223, thanks to m16a1] * Java scanner: Add @var@ as type. [#229, thanks to Davide Angelocola] * Gem: Fix deprecation warning. [#246, thanks to David Rodríguez] h2. Changes in 1.1.2 * Ruby future: Add support for frozen string literals. [#211, thanks to Pat Allan] * C++ scanner: Add C++11 keywords. [#195, thanks to Johnny Willemsen] * Haml scanner: Allow @-@ in tags. * Java scanner: Allow Unicode characters in identifiers. [#212, thanks to t-gergely] h2. Changes in 1.1.1 * SQL scanner: Allow @$@ signs in SQL identifiers [#164, thanks to jasir and Ben Basson] * SQL scanner: Fix open strings [#163, thanks to Adam] * Ruby scanner: Accept number literal suffixes @r@ and @i@ (Ruby 2.1) * Ruby scanner: Accept quoted hash keys like @{ "a": boss }@ (Ruby 2.2) * Ruby scanner: Accept save navigation operator @&.@ (Ruby 2.3) * Ruby scanner: Accept squiggly heredoc @<<~@ (Ruby 2.3) * Diff scanner: Prevent running out of regexp stack. * HTML encoder: You can keep tabs intact now by setting @tab_width: false@. * Alpha style: Tweaked colors for @:function@ group with @:content@. * File structure: One module per file, autoload CodeRay::Version, paths follow namespace hierarchy. h2. Changes in 1.1 New scanners: * Go [#28, thanks to Eric Guo and Nathan Youngman] * Lua [#21, #22, thanks to Quintus] * Sass [#93] * Taskpaper [#39, thanks to shimomura] More new stuff: * @.xaml@ file type [#121, thanks to Kozman Bálint] * recognize @Guardfile@, @Vagrantfile@, and @Appraisals@ as Ruby files [#121, thanks to Kozman Bálint] * new token kind @:id@ for CSS/Sass [#27] * new token kind @:done@ for Taskpaper [#39] * new token kind @:map@ for Lua, introducing a nice nested-shades trick [#22, thanks to Quintus and Nathan Youngman] * new token kind @:unknown@ for Debug scanner * new DebugLint encoder that checks for empty tokens and correct nesting Improvements: * CSS scanner uses @:id@ and @:tag@ now [#27] * Diff scanner: Highlight inline changes in multi-line changes [#99] * JavaScript scanner: Highlight multi-line comments in diff correctly * JSON scanner: simplify key/value heuristic, using look-ahead instead of a stack * HTML scanner displays style tags and attributes now [#145] * Ruby scanner: Accept @%i(…)@ and @%I(…)@ symbol lists (Ruby 2.0) [thanks to Nathan Youngman] * Ruby scanner: Accept keywords as Ruby hash keys [#126] * performance improvements to several scanners and encoders, especially Terminal and HTML * added @:keep_state@ functionality to more scanners so they work nicely with diff now [#116] * refactoring and cleanup to achieve better "Code Climate" ratings (but I don't really care) * updated and cleaned up the documentation, * documented list of TokenKinds * Alpha style: tweaked colors for @.binary@, @.local-variable@, and @.predefined-type@ * @rake generate@ supports Git now instead of Subversion Removed: * @Tokens#dump@, @Tokens.load@, @Tokens::Undumping@, and @zlib@ dependency * double-click toggle handler from HTML table output * @rake_helpers@, @sample@ directories and several other ancient garbage Fixes: * fixes to CSS scanner (floats, pseudoclasses, nth-child) [#143] * fixed empty tokens and unclosed token groups in HTML, CSS, Diff, Goovy, PHP, Raydebug, Ruby, SQL, and YAML scanners [#144] * fixed @:docstring@ token type style * fixed several infinite Hash caches and dynamic Symbol creation that might have been exploited by an attacker [#148] * fixed HTML encoder when output is a StringIO (eg. when using @-HTML@ as a command line parameter) * TokenKinds should not be frozen [#130, thanks to Gavin Kistner] * display line numbers in HTML @:table@ mode even for single-line code (remove special case) [#41, thanks to Ariejan de Vroom] * override Bootstrap's @pre { word-break: break-all }@ styling for line numbers [#102, thanks to lightswitch05] * HTML encoder will not warn about unclosed token groups at the end of the stream * fixed problem with coderay/version.rb being loaded twice Internals: * The Debug scanner maps unknown token kinds to @:unknown@ (to avoid creating Symbols based on possibly unsafe input). * The Raydebug scanner highlights unknown token kinds as @:plain@. * The Debug encoder refactored; use DebugLint if you want strict checking now.. * The Debug encoder will not warn about errors in the token stream. * Plugin does not warn about fallback when default is defined. * PluginHost now works with Strings instead of Symbols internally (to avoid using @#to_sym@). h2. Changes in 1.0.9 * Fix Ruby scanner: Ruby 1.9 hash syntax @{ key: value }@ is highlighted correctly. [GH #106, thanks to Seth Vargo] * Fix HTML scanner: Accept DTDs. [GH #83] * Fix PHP scanner: Accept Unicode. [GH #40, thanks to Lance Li] h2. Changes in 1.0.8 * add @:string/:char@, remove @:regexp/:function@ color from Terminal encoder [GH #29, thanks to Kyrylo Silin] * allow @-@ in line number anchor prefix for HTML encoder [GH #32, thanks to shurizzle] * Fix HTML scanner: Don't crash if HTML in a diff contains a JavaScript tag. h2. Changes in 1.0.7 * Changed license from LGPL to MIT. [GH-25, thanks to jessehu] * Fix issue with plugin files not being loaded. [GH-20, thanks to Will Read] * Fix HTML scanner bug: Don't choke on boolean attributes. [GH-26, thanks to jugglinmike] h2. Changes in 1.0.6 * New option @:break_lines@ for the HTML encoder (splits tokens at line breaks). [GH-15, thanks to Etienne Massip] * Improved speed of @:line_numbers => :inline@ option for the HTML encoder. * Fixed wrong HTML file type. (was @:page@) [GH-16, thanks to Doug Hammond] * The CSS Scanner now highlights tokens like @url(...)@ as @:function@ instead of @:string@. [GH-13, thanks to Joel Holdbrooks] h2. Changes in 1.0.5 Fixes: * @autoload@ calls do not depend on @coderay/lib@ being in the load path (GitHub issue #6; thanks to tvon, banister, envygeeks, and ConradIrwin) * avoid dark blue as terminal color (GitHub issue #9; thanks to shevegen) h2. Changes in 1.0.4 Fixes in the CSS scanner: * understands the unit "s" (seconds) * ignores unexpected curly braces * code inside of diffs is highlighted correctly h2. Changes in 1.0.3 New: * .tmproj files are recognized as XML. Fixes: * Removed files are highlighted inside diffs generated by git. h2. Changes in 1.0.2 Fixes: * .erb files are recognized as ERB. h2. Changes in 1.0.1 New: * YAML scanner allows "-" and "/" in key names Changes: * HTML page output has no white border anymore (alpha style) Fixes: * fixed warning in the output of "coderay stylesheet" * fixed additional scrollbar in code when last line contains an eyecatcher * minor fixes in the tests (issue github-#4) h2. Changes in 1.0 CodeRay 1.0 is a major rewrite of the library, and incompatible to earlier versions. The command line and programmer interfaces are similar to 0.9, but the internals have completely changed. h3. General changes * *NEW*: The new Diff scanner colorizes code inside of the diff, and highlights inline changes. * *NEW*: Extended support and usage of HTML5 and CSS 3 features. * *NEW*: Direct Streaming * *NEW* scanners: Clojure and HAML * *CHANGED*: Token classes (used as CSS classes) are readable names; breaks you stylesheet! * *IMPROVED* documentation * *IMPROVED* speed: faster startup (using @autoload@), scanning, and encoding * *IMPROVED* Ruby 1.9 encodings support * *IMPROVED* Tests: There are more of them now! h3. Direct Streaming CodeRay 1.0 introduces _Direct Streaming_ as a faster and simpler alternative to Tokens. It means that all Scanners, Encoders and Filters had to be rewritten, and that older scanners using the Tokens API are no longer compatible with this version. The main benefits of this change are: * more speed (benchmarks show 10% to 50% more tokens per second compared to CodeRay 0.9) * the ability to stream output into a pipe on the command line * a simpler API * less code Changes related to the new tokens handling include: * *CHANGED*: The Scanners now call Encoders directly; tokens are not added to a Tokens array, but are send to the Encoder as a method call. The Tokens representation (which can be seen as a cache now) is still present, but as a special case; Tokens just encodes the given tokens into an Array for later use. * *CHANGED*: The token actions (@text_token@, @begin_group@ etc.) are now public methods of @Encoder@ and @Tokens@. * *REWRITE* of all Scanners, Encoders, Filters, and Tokens. * *RENAMED* @:open@ and @:close@ actions to @:begin_group@ and @:end_group@. * *RENAMED* @open_token@ and @close_token@ methods to @begin_group@ and @end_group@. * *NEW* method @#tokens@ allows to add several tokens to the stream. @Tokens@ and @Encoders::Encoder@ define this method. * *CHANGED* The above name changes also affect the JSON, XML, and YAML encoders. CodeRay 1.0 output will be incompatible with earlier versions. * *REMOVED* @TokenStream@ and the @Streamable@ API and all related features like @NotStreamableError@ are now obsolete and have been removed. h3. Command Line The @coderay@ executable was rewritten and has a few new features: * *NEW* Ability to stream into a pipe; try @coderay file | more -r@ * *NEW* help * *IMPROVED*: more consistent parameter handling * *REMOVED* @coderay_stylesheet@ executable; use @coderay stylesheet [name]@. h3. @Tokens@ * *NEW* methods @count@, @begin_group@, @end_group@, @begin_line@, and @end_line@. * *REMOVED* methods @#stream?@, @#each_text_token@. * *REMOVED* methods @#optimize@, @#fix@, @#split_into_lines@ along with their bang! variants. * *REMOVED* @#text@ and @#text_size@ methods. Use the @Text@ encoder instead. * *REMOVED* special implementation of @#each@ taking a filter parameter. Use @TokenKindFilter@ instead. h3. *RENAMED*: @TokenKinds@ Renamed from @Tokens::ClassOfKind@ (was also @Tokens::AbbreviationForKind@ for a while). The term "token class" is no longer used in CodeRay. Instead, tokens have _kinds_. See "#122":http://odd-eyed-code.org/issues/122. * *CHANGED* all token CSS classes to readable names. * *ADDED* token kinds @:filename@, @:namespace@, and @:eyecatcher@. * *RENAMED* @:pre_constant@ and @:pre_type@ to @:predefined_constant@ and @predefined_type@. * *RENAMED* @:oct@ and @:bin@ to @:octal@ and @binary@. * *REMOVED* token kinds @:attribute_name_fat@, @:attribute_value_fat@, @:operator_fat@, @interpreted@, @:tag_fat@, @tag_special@, @:xml_text@, @:nesting_delimiter@, @:open@, and @:close@. * *CHANGED*: Don't raise error for unknown token kinds unless in @$CODERAY_DEBUG@ mode. * *CHANGED* the value for a token kind that is not highlighted from @:NO_HIGHLIGHT@ to @false@. h3. @Duo@ * *NEW* method @call@ for allowing code like @CodeRay::Duo[:python => :yaml].(code)@ in Ruby 1.9. h3. @Encoders::CommentFilter@ * *NEW* alias @:remove_comments@ h3. @Encoders::Filter@ * *NEW* option @tokens@. * *CHANGED*: Now it simply delegates to the output. * *REMOVED* @include_text_token?@ and @include_block_token?@ methods. h3. @Encoders::HTML@ The HTML encoder was cleaned up and simplified. * *NEW*: HTML5 and CSS 3 compatible. See "#215":http://odd-eyed-code.org/issues/215. * *ADDED* support for @:line_number_anchors@. See "#208":http://odd-eyed-code.org/issues/208. * *CHANGED* the default style to @:alpha@. * *CHANGED*: Use double click to toggle line numbers in table mode (as single click jumps to an anchor.) * *REMOVED* support for @:line_numbers => :list@. * *FIXED* splitting of lines for @:line_numbers => :inline@, so that the line numbers don't get colored, too. * *RENAMED* @Output#numerize@ to @#number@, which is an actual English word. h3. @Encoders::LinesOfCode@ * *CHANGED*: @compile@ and @finish@ methods are now protected. h3. *Renamed*: @Encoders::Terminal@ (was @Encoders::Term@) * *RENAMED* from @Encoders::Term@, added @:term@ alias. * *CLEANUP*: Use @#setup@'s @super@, don't use @:procedure@ token class. * *CHANGED*: @#token@'s second parameter is no longer optional. * *REMOVED* colors for obsolete token kinds. * *FIXED* handling of line tokens. h3. @Encoders::Text@ * *FIXED* default behavior of stripping the trailing newline. h3. *RENAMED*: @Encoders::TokenKindFilter@ (was @Encoders::TokenClassFilter@) * *NEW*: Handles token groups. See "#223":http://odd-eyed-code.org/issues/223. * *RENAMED* @include_block_token?@ to @include_group?@. h3. @Encoders::Statistic@ * *CHANGED*: Tokens actions are counted separately. h3. @Scanners::Scanner@ * *NEW* methods @#file_extension@ and @#encoding@. * *NEW*: The @#tokenize@ method also takes an Array of Strings as source. The code is highlighted as one and split into parts of the input lengths after that using @Tokens#split_into_parts@. * *NEW* method @#binary_string@ * *REMOVED* helper method @String#to_unix@. * *REMOVED* method @#streamable?@. * *REMOVED* @#marshal_load@ and @#marshal_dump@. * *RENAMED* class method @normify@ to @normalize@; it also deals with encoding now. * *CHANGED*: @#column@ starts counting with 1 instead of 0 h3. *NEW*: @Scanners::Clojure@ Thanks to Licenser, CodeRay now supports the Clojure language. h3. @Scanners::CSS@ * *NEW*: Rudimentary support for the @attr@, @counter@, and @counters@ functions. See "#224":http://odd-eyed-code.org/issues/224. * *NEW*: Rudimentary support for CSS 3 colors. * *CHANGED*: Attribute selectors are highlighted as @:attribute_name@ instead of @:string@. * *CHANGED*: Comments are scanned as one token instead of three. h3. @Scanners::Debug@ * *NEW*: Support for line tokens (@begin_line@ and @end_line@ represented by @[@ and @]@.) * *FIXED*: Don't send @:error@ and @nil@ tokens for buggy input any more. * *FIXED*: Closes unclosed tokens at the end of @scan_tokens@. * *IMPROVED*: Highlight unknown tokens as @:error@. * *CHANGED*: Raises an error when trying to end an invalid token group. h3. @Scanners::Delphi@ * *FIXED*: Closes open string groups. h3. @Scanners::Diff@ * *NEW*: Highlighting of code based on file names. See ticket "#52":http://odd-eyed-code.org/issues/52. Use the @:highlight_code@ option to turn this feature off. It's enabled by default. This is a very original feature. It enables multi-language highlighting for diff files, which is especially helpful for CodeRay development itself. The updated version of the scanner test suite generated .debug.diff.html files using this. Note: This is still experimental. Tokens spanning more than one line may get highlighted incorrectly. CodeRay tries to keep scanner states between the lines and changes, but the quality of the results depend on the scanner. * *NEW*: Inline change highlighting, as suggested by Eric Thomas. See ticket "#227":http://odd-eyed-code.org/issues/227 for details. Use the @:inline_diff@ option to turn this feature off. It's enabled by default. For single-line changes (that is, a single deleted line followed by a single inserted line), this feature surrounds the changed parts with an @:eyecatcher@ group which appears in a more saturated background color. The implementation is quite complex, and highly experimental. The problem with multi-layer tokenizing is that the tokens have to be split into parts. If the inline change starts, say, in the middle of a string, then additional @:end_group@ and @:begin_group@ tokens must be inserted to keep the group nesting intact. The extended @Scanner#tokenize@ method and the new @Tokens#split_into_parts@ method take care of this. * *NEW*: Highlight the file name in the change headers as @:filename@. * *CHANGED*: Highlight unknown lines as @:comment@ instead of @:head@. * *IMPROVED*: Background colors for Diff output have been optimized. h3. *RENAMED*: @Scanners::ERB@ (was @Scanners::RHTML@) h3. *NEW*: @Scanners::HAML@ It uses the new :state options of the HTML and Ruby scanners. Some rare cases are not considered (like @#{...}@ snippets inside of :javascript blocks), but it highlights pretty well. h3. @Scanners::HTML@ * *FIXED*: Closes open string groups. h3. @Scanners::JavaScript@ * *IMPROVED*: Added @NaN@ and @Infinity@ to list of predefined constants. * *IMPROVED* recognition of RegExp literals with leading spaces. h3. @Scanners::Java@ * *NEW*: Package names are highlighted as @:namespace@. See "#210":http://odd-eyed-code.org/issues/210. h3. *REMOVED*: @Scanners::NitroXHTML@ Nitro is "dead":http://www.nitrohq.com/. h3. *RENAMED*: @Scanners::Text@ (was @Scanners::Plaintext@) * *IMPROVED*: Just returns the string without scanning (faster). This is much faster than scanning until @/\z/@ in Ruby 1.8. h3. @Scanners::Python@ * *CHANGED*: Docstrings are highlighted as @:comment@. See "#190":http://odd-eyed-code.org/issues/190. h3. *NEW*: @Scanners::Raydebug@ Copied from @Scanners::Debug@, highlights the token dump instead of importing it. It also reacts to the @.raydebug@ file name suffix now. h3. @Scanners::Ruby@ * *ADDED* more predefined keywords (see http://murfy.de/ruby-constants). * *IMPROVED* support for singleton method definitions. See "#147":http://odd-eyed-code.org/issues/147. * *FIXED*: Don't highlight methods with a capital letter as constants (eg. @GL.PushMatrix@). * *NEW*: Highlight buggy floats (like .5) as @:error@. * *CLEANUP* of documentation, names of constants and variables, state handling. Moved @StringState@ class from @patterns.rb@ into a separate file. * *NEW*: Complicated rule for recognition of @foo=@ style method names. * *NEW*: Handles @:keep_state@ option (a bit; experimental). Actually, Ruby checks if there is @[~>=]@, but not @=>@ following the name. * *REMOVED* @EncodingError@ h3. *REMOVED* @Scanners::Scheme@ * It is too buggy, and nobody was using it. To be added again when it's fixed. See "#59":http://odd-eyed-code.org/issues/59. h3. @Scanners::SQL@ * *IMPROVED*: Extended list of keywords and functions (thanks to Joshua Galvez, Etienne Massip, and others). See "#221":http://odd-eyed-code.org/issues/221. * *FIXED*: Closes open string groups. * *FIXED*: Words after @.@ are always recognized as @:ident@. h3. @Scanners::YAML@ * *FIXED*: Allow spaces before colon in mappings. See "#231":http://odd-eyed-code.org/issues/231. h3. *NEW*: @Styles::Alpha@ A style that uses transparent HSLA colors as defined in CSS 3. See "#199":http://odd-eyed-code.org/issues/199. It also uses the CSS 3 property @user-select: none@ to keep the user from selecting the line numbers. This is especially nice for @:inline@ line numbers. See "#226":http://odd-eyed-code.org/issues/226. h3. @WordList@ Stripped down to 19 LOC. * *RENAMED* @CaseIgnoringWordList@ to @WordList::CaseIgnoring@. * *REMOVED* caching option because it creates memory leaks. * *REMOVED* block option. h3. @FileType@ * *NEW*: Recognizes @.gemspec@, @.rjs@, @.rpdf@ extensions, @Gemfile@, and @Capfile@ as Ruby. Thanks to the authors of the TextMate Ruby bundle! * *REMOVED* @FileType#shebang@ is a protected method now. h3. @Plugin@ * *IMPROVED*: @register_for@ sets the @plugin_id@; it can now be a @Symbol@. * *ADDED* @PluginHost#const_missing@ method: Plugins are loaded automatically. Using @Scanners::JavaScript@ in your code loads @scanners/java_script.rb@. * *ADDED* @#all_plugins@ method to simplify getting information about all available plugins (suggested by bnhymn). * *CHANGED* the default plugin key from @nil@ to @:default@. h3. @GZip@ * *MOVED* into @CodeRay@ namespace. * *MOVED* file from @gzip_simple.rb@ to @gzip.rb@. * *REMOVED* @String@ extensions. h3. More API changes * *FIXED* @Encoders::HTML#token@'s second parameter is no longer optional. * *CHANGED* @Encoders::HTML::Output@'s API. * *REMOVED* lots of unused methods. The helper classes were cleaned up; see above for details. * *CHANGED* @Plugin@ API was simplified and stripped of all unnecessary features. * *CHANGED* Moved @GZip@ and @FileType@ libraries into @CodeRay@; cleaned them up. coderay-1.1.3/FOLDERS000066400000000000000000000020551366440446100141750ustar00rootroot00000000000000= CodeRay - folder structure == bench - Benchmarking system All benchmarking stuff goes here. Test inputs are stored in files named example.. Test outputs go to bench/test.. Run bench/bench.rb to get a usage description. Run rake bench to perform an example benchmark. == bin - Scripts Executional files for CodeRay. coderay:: The CodeRay executable. == demo - Demos and functional tests Demonstrational scripts to show of CodeRay's features. Run them as functional tests with rake test:demos. == etc - Lots of stuff Some additional files for CodeRay, mainly graphics and Vim scripts. == lib - CodeRay library code This is the base directory for the CodeRay library. == rake_helpers - Rake helper libraries Some files to enhance Rake, including the Autumnal Rdoc template and some scripts. == test - Tests In the subfolder scanners/ are the scanners tests. Each language has its own subfolder and sub-suite. Run with rake test. coderay-1.1.3/Gemfile000066400000000000000000000017341366440446100144520ustar00rootroot00000000000000source 'https://rubygems.org' # Specify your gem's dependencies in coderay.gemspec gemspec # Add dependencies to develop your gem here. # Include everything needed to run rake, tests, features, etc. group :development do gem 'bundler' gem 'json', '~> 1.8' if RUBY_VERSION < '2.0' gem 'rake', RUBY_VERSION < '1.9' ? '~> 10.5' : '>= 10.5' gem 'rdoc', Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.3') ? '~> 4.2.2' : Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.2') ? '< 6' : '>= 6' gem 'RedCloth', RUBY_PLATFORM == 'java' ? '= 4.2.9' : '>= 4.0.3' gem 'rspec', '~> 3.9.0' gem 'shoulda-context', RUBY_VERSION < '1.9' ? '= 1.2.1' : '>= 1.2.1' gem 'simplecov', '~> 0.17.1' gem 'term-ansicolor', RUBY_VERSION < '2.0' ? '~> 1.3.2' : '>= 1.3.2' gem 'test-unit', RUBY_VERSION < '1.9' ? '~> 2.0' : '>= 3.0' gem 'tins', RUBY_VERSION < '2.0' ? '~> 1.6.0' : '>= 1.6.0' end coderay-1.1.3/MIT-LICENSE000066400000000000000000000021551366440446100146110ustar00rootroot00000000000000Copyright (C) 2005-2012 Kornelius Kalnbach (@murphy_karasu) http://coderay.rubychan.de/ 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.coderay-1.1.3/README.markdown000066400000000000000000000016041366440446100156540ustar00rootroot00000000000000# CodeRay [![Build Status](https://travis-ci.org/rubychan/coderay.svg?branch=master)](https://travis-ci.org/rubychan/coderay) [![Gem Version](https://badge.fury.io/rb/coderay.svg)](https://badge.fury.io/rb/coderay) [![Maintainability](https://api.codeclimate.com/v1/badges/e015bbd5eab45d948b6b/maintainability)](https://codeclimate.com/github/rubychan/coderay/maintainability) ## About CodeRay is a Ruby library for syntax highlighting. You put your code in, and you get it back colored; Keywords, strings, floats, comments - all in different colors. And with line numbers. ## Installation `gem install coderay` ### Dependencies CodeRay needs Ruby 1.8.7, 1.9.3 or 2.0+. It also runs on JRuby. ## Example Usage ```ruby require 'coderay' html = CodeRay.scan("puts 'Hello, world!'", :ruby).div(:line_numbers => :table) ```` ## Documentation See [rubydoc](http://rubydoc.info/gems/coderay). coderay-1.1.3/README_INDEX.rdoc000066400000000000000000000107231366440446100157120ustar00rootroot00000000000000= CodeRay Tired of blue'n'gray? Try the original version of this documentation on coderay.rubychan.de[http://coderay.rubychan.de/doc/] :-) == About CodeRay is a Ruby library for syntax highlighting. You put your code in, and you get it back colored; Keywords, strings, floats, comments - all in different colors. And with line numbers. *Syntax* *Highlighting*... * makes code easier to read and maintain * lets you detect syntax errors faster * helps you to understand the syntax of a language * looks nice * is what everybody wants to have on their website * solves all your problems and makes the girls run after you == Installation % gem install coderay === Dependencies CodeRay needs Ruby 1.8.7+ or 1.9.2+. It also runs on Rubinius and JRuby. == Example Usage require 'coderay' html = CodeRay.scan("puts 'Hello, world!'", :ruby).div(:line_numbers => :table) == Documentation See CodeRay. == Credits === Special Thanks to * licenser (Heinz N. Gies) for ending my QBasic career, inventing the Coder project and the input/output plugin system. CodeRay would not exist without him. * bovi (Daniel Bovensiepen) for helping me out on various occasions. === Thanks to * Caleb Clausen for writing RubyLexer (see http://rubyforge.org/projects/rubylexer) and lots of very interesting mail traffic * birkenfeld (Georg Brandl) and mitsuhiku (Arnim Ronacher) for PyKleur, now pygments. You guys rock! * Jamis Buck for writing Syntax (see http://rubyforge.org/projects/syntax) I got some useful ideas from it. * Doug Kearns and everyone else who worked on ruby.vim - it not only helped me coding CodeRay, but also gave me a wonderful target to reach for the Ruby scanner. * everyone who uses CodeBB on http://www.rubyforen.de and http://www.python-forum.de * iGEL, magichisoka, manveru, WoNáDo and everyone I forgot from rubyforen.de * Dethix from ruby-mine.de * zickzackw * Dookie (who is no longer with us...) and Leonidas from http://www.python-forum.de * Andreas Schwarz for finding out that CaseIgnoringWordList was not case ignoring! Such things really make you write tests. * closure for the first version of the Scheme scanner. * Stefan Walk for the first version of the JavaScript and PHP scanners. * Josh Goebel for another version of the JavaScript scanner, a SQL and a Diff scanner. * Jonathan Younger for pointing out the licence confusion caused by wrong LICENSE file. * Jeremy Hinegardner for finding the shebang-on-empty-file bug in FileType. * Charles Oliver Nutter and Yehuda Katz for helping me benchmark CodeRay on JRuby. * Andreas Neuhaus for pointing out a markup bug in coderay/for_redcloth. * 0xf30fc7 for the FileType patch concerning Delphi file extensions. * The folks at redmine.org - thank you for using and fixing CodeRay! * Keith Pitt for his SQL scanners * Rob Aldred for the terminal encoder * Trans for pointing out $DEBUG dependencies * Flameeyes for finding that Term::ANSIColor was obsolete * matz and all Ruby gods and gurus * The inventors of: the computer, the internet, the true color display, HTML & CSS, VIM, Ruby, pizza, microwaves, guitars, scouting, programming, anime, manga, coke and green ice tea. Where would we be without all those people? === Created using * Ruby[http://ruby-lang.org/] * Chihiro (my Sony VAIO laptop); Henrietta (my old MacBook); Triella, born Rico (my new MacBook); as well as Seras and Hikari (my PCs) * RDE[http://homepage2.nifty.com/sakazuki/rde_e.html], VIM[http://vim.org] and TextMate[http://macromates.com] * Subversion[http://subversion.tigris.org/] * Redmine[http://redmine.org/] * Firefox[http://www.mozilla.org/products/firefox/], Firebug[http://getfirebug.com/], Safari[http://www.apple.com/safari/], and Thunderbird[http://www.mozilla.org/products/thunderbird/] * RubyGems[http://docs.rubygems.org/] and Rake[http://rake.rubyforge.org/] * TortoiseSVN[http://tortoisesvn.tigris.org/] using Apache via XAMPP[http://www.apachefriends.org/en/xampp.html] * RDoc (though I'm quite unsatisfied with it) * Microsoft Windows (yes, I confess!) and MacOS X * GNUWin32, MinGW and some other tools to make the shell under windows a bit less useless * Term::ANSIColor[http://term-ansicolor.rubyforge.org/] * PLEAC[http://pleac.sourceforge.net/] code examples * Github * Travis CI (http://travis-ci.org/rubychan/github) === Free * As you can see, CodeRay was created under heavy use of *free* software. * So CodeRay is also *free*. * If you use CodeRay to create software, think about making this software *free*, too. * Thanks :) coderay-1.1.3/Rakefile000066400000000000000000000015171366440446100146230ustar00rootroot00000000000000require 'bundler/gem_tasks' $:.unshift File.dirname(__FILE__) unless $:.include? '.' ROOT = '.' LIB_ROOT = File.join ROOT, 'lib' task :default => :test if File.directory? 'rake_tasks' # load rake tasks from subfolder for task_file in Dir['rake_tasks/*.rake'].sort load task_file end else # fallback tasks when rake_tasks folder is not present (eg. in the distribution package) desc 'Run CodeRay tests (basic)' task :test do ruby './test/functional/suite.rb' ruby './test/functional/for_redcloth.rb' end gem 'rdoc' if defined? gem require 'rdoc/task' desc 'Generate documentation for CodeRay' Rake::RDocTask.new :doc do |rd| rd.title = 'CodeRay Documentation' rd.main = 'README_INDEX.rdoc' rd.rdoc_files.add Dir['lib'] rd.rdoc_files.add rd.main rd.rdoc_dir = 'doc' end end coderay-1.1.3/bench/000077500000000000000000000000001366440446100142315ustar00rootroot00000000000000coderay-1.1.3/bench/bench.rb000066400000000000000000000027121366440446100156370ustar00rootroot00000000000000require 'benchmark' $: << File.expand_path('../../lib', __FILE__) require 'coderay' if ARGV.include? '-h' puts DATA.read exit end lang = ARGV.fetch(0, 'ruby') data = nil File.open(File.expand_path("../example.#{lang}", __FILE__), 'rb') { |f| data = f.read } raise 'Example file is empty.' if data.empty? format = ARGV.fetch(1, 'html').downcase encoder = CodeRay.encoder(format) size = ARGV.fetch(2, 3000).to_i * 1000 unless size.zero? data += data until data.size >= size data = data[0, size] end size = data.size puts "encoding %d kB of #{lang} code to #{format}..." % [(size / 1000.0).round] n = ARGV.fetch(3, 10).to_s[/\d+/].to_i require 'profile' if ARGV.include? '-p' times = [] n.times do |i| time = Benchmark.realtime { encoder.encode(data, lang) } puts "run %d: %5.2f s, %4.0f kB/s" % [i + 1, time, size / time / 1000.0] times << time end times_sum = times.inject(0) { |time, sum| sum + time } puts 'Average time: %5.2f s, %4.0f kB/s' % [times_sum / times.size, (size * n) / times_sum / 1000.0] puts 'Best time: %5.2f s, %4.0f kB/s' % [times.min, size / times.min / 1000.0] __END__ Usage: ruby bench.rb [lang] [format] [size in kB] [number of runs] - lang defaults to ruby. - format defaults to html. - size defaults to 1000 kB (= 1,000,000 bytes). 0 uses the whole example input. - number of runs defaults to 5. -h prints this help -p generates a profile (slow, use with SIZE = 1) -w waits after the benchmark (for debugging memory usw) coderay-1.1.3/bench/example.ruby000066400000000000000000007650361366440446100166100ustar00rootroot00000000000000module 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: rweb.rb 6 2004-06-16 15:56:26Z igel $ # # == 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 class Parser def Parser.flatten str # replace mac & dos newlines with unix style str.gsub(/\r\n?/, "\n") end def initialize input = '' # input manager @scanner = StringScanner.new '' # output manager @encoder = Encoder.new @output = '' # tag manager @tagstack = TagStack.new(@encoder) @do_magic = true # set the input feed input end # if you want, you can feed a parser instance after creating, # or even feed it repeatedly. def feed food @scanner.string = Parser.flatten food end # parse through the string using parse_token def parse parse_token until @scanner.eos? @tagstack.close_all @output = parse_magic @encoder.output end def output @output end # ok, internals start here private # the default output functions. everything should use them or the tags. def add_text text = @scanner.matched @encoder.add_text text end # use this carefully def add_html html @encoder.add_html html end # highlights the text as error def add_garbage garbage add_html '' if DEBUG add_text garbage add_html '' if DEBUG end # unknown and incorrectly nested tags are ignored and # sent as plaintext (garbage in - garbage out). # in debug mode, garbage is marked with lime background. def garbage_out start @scanner.pos = start garbage = @scanner.scan(/./m) debug 'GARBAGE: ' + garbage add_garbage garbage end # simple text; everything but [, \[ allowed SIMPLE_TEXT_SCAN_ = / [^\[\\]* # normal* (?: # ( \\.? # special [^\[\\]* # normal* )* # )* /mx SIMPLE_TEXT_SCAN = /[^\[]+/ =begin WHAT IS A TAG? ============== Tags in BBCode can be much more than just a simple [b]. I use many terms here to differ the parts of each tag. Basic scheme: [ code ] TAG START TAG INFO TAG END Most tags need a second tag to close the range it opened. This is done with CLOSING TAGS: [/code] or by using empty tags that have no content and close themselfes: [url=winamp.com /] You surely know this from HTML. These slashes define the TAG KIND = normal|closing|empty and cannot be used together. Everything between [ and ] and expluding the slashes is called the TAG INFO. This info may contain: - TAG ID - TAG NAME including the tag id - attributes The TAG ID is the first char of the info: TAG | ID ----------+---- [quote] | q [±] | & ["[b]"] | " [/url] | u [---] | - As you can see, the tag id shows the TAG TYPE, it can be a normal tag, a formatting tag or an entity. Therefor, the parser first scans the id to decide how to go on with parsing. =end # tag # TODO more complex expression allowing # [quote="[ladico]"] and [quote=\[ladico\]] to be correct tags TAG_BEGIN_SCAN = / \[ # tag start ( \/ )? # $1 = closing tag? ( [^\]] ) # $2 = tag id /x TAG_END_SCAN = / [^\]]* # rest that was not handled \]? # tag end /x CLOSE_TAG_SCAN = / ( [^\]]* ) # $1 = the rest of the tag info ( \/ )? # $2 = empty tag? \]? # tag end /x UNCLOSED_TAG_SCAN = / \[ /x CLASSIC_TAG_SCAN = / [a-z]* /ix SEPARATOR_TAG_SCAN = / \** /x FORMAT_TAG_SCAN = / -- -* /x QUOTED_SCAN = / ( # $1 = quoted text [^"\\]* # normal* (?: # ( \\. # special [^"\\]* # normal* )* # )* ) "? # end quote " /mx ENTITY_SCAN = / ( [^;\]]+ ) # $1 = entity code ;? # optional ending semicolon /ix SMILEY_SCAN = Smileys::SMILEY_PATTERN # this is the main parser loop that separates # text - everything until "[" # from # tags - starting with "[", ending with "]" def parse_token if @scanner.scan(SIMPLE_TEXT_SCAN) add_text else handle_tag end end def handle_tag tag_start = @scanner.pos unless @scanner.scan TAG_BEGIN_SCAN garbage_out tag_start return end closing, id = @scanner[1], @scanner[2] #debug 'handle_tag(%p)' % @scanner.matched handled = case id when /[a-z]/i if @scanner.scan(CLASSIC_TAG_SCAN) if handle_classic_tag(id + @scanner.matched, closing) already_closed = true end end when '*' if @scanner.scan(SEPARATOR_TAG_SCAN) handle_asterisk tag_start, id + @scanner.matched true end when '-' if @scanner.scan(FORMAT_TAG_SCAN) #format = id + @scanner.matched @encoder.add_html "\n
\n" true end when '"' if @scanner.scan(QUOTED_SCAN) @encoder.add_text unescape(@scanner[1]) true end when '&' if @scanner.scan(ENTITY_SCAN) @encoder.add_entity @scanner[1] true end when Smileys::SMILEY_START_CHARSET @scanner.pos = @scanner.pos - 1 # (ungetch) if @scanner.scan(SMILEY_SCAN) @encoder.add_html Smileys.smiley_to_image(@scanner.matched) true end end # case return garbage_out(tag_start) unless handled @scanner.scan(TAG_END_SCAN) unless already_closed end ATTRIBUTES_SCAN = / ( [^\]"\\]* (?: (?: \\. | " [^"\\]* (?: \\. [^"\\]* )* "? ) [^\]"\\]* )* ) \]? /x def handle_classic_tag name, closing debug 'TAG: ' + (closing ? '/' : '') + name # flatten name.downcase! tag_class = TAG_LIST[name] return unless tag_class #debug((opening ? 'OPEN ' : 'CLOSE ') + tag_class.name) # create an attribute object to handle it @scanner.scan(ATTRIBUTES_SCAN) #debug name + ':' + @scanner[1] attr = Attribute.create @scanner[1] #debug 'ATTRIBUTES %p ' % attr #unless attr.empty? #debug 'closing: %p; name=%s, attr=%p' % [closing, name, attr] # OPEN if not closing and tag = @tagstack.try_open_class(tag_class, attr) #debug 'opening' tag.do_open @scanner # this should be done by the tag itself. if attr.empty_tag? tag.handle_empty @tagstack.close_tag elsif tag.special_content? handle_special_content(tag) @tagstack.close_tag # # ignore asterisks directly after the opening; these are phpBBCode # elsif tag.respond_to? :asterisk # debug 'SKIP ASTERISKS: ' if @scanner.skip(ASTERISK_TAGS_SCAN) end # CLOSE elsif @tagstack.try_close_class(tag_class) #debug 'closing' # GARBAGE else return end true end def handle_asterisk tag_start, stars #debug 'ASTERISK: ' + stars.to_s # rule for asterisk tags: they belong to the last tag # that handles them. tags opened after this tag are closed. # if no open tag uses them, all are closed. tag = @tagstack.close_all_until { |tag| tag.respond_to? :asterisk } unless tag and tag.asterisk stars, @scanner garbage_out tag_start end end def handle_special_content tag scanned = @scanner.scan_until(tag.closing_tag) if scanned scanned.slice!(-(@scanner.matched.size)..-1) else scanned = @scanner.scan(/.*/m).to_s end #debug 'SPECIAL CONTENT: ' + scanned tag.handle_content(scanned) end def unescape text # input: correctly formatted quoted string (without the quotes) text.gsub(/\\(?:(["\\])|.)/) { $1 or $& } end # MAGIC FEAUTURES URL_PATTERN = /(?:(?:www|ftp)\.|(?>\w{3,}):\/\/)\S+/ EMAIL_PATTERN = /(?>[\w\-_.]+)@[\w\-\.]+\.\w+/ HAS_MAGIC = /[&@#{Smileys::SMILEY_START_CHARS}]|(?i:www|ftp)/ MAGIC_PATTERN = Regexp.new('(\W|^)(%s)' % [Smileys::MAGIC_SMILEY_PATTERN, URL_PATTERN, EMAIL_PATTERN].map { |pattern| pattern.to_s }.join('|') ) IS_SMILEY_PATTERN = Regexp.new('^%s' % Smileys::SMILEY_START_CHARSET.to_s ) IS_URL_PATTERN = /^(?:(?i:www|ftp)\.|(?>\w+):\/\/)/ URL_STARTS_WITH_PROTOCOL = /^\w+:\/\// IS_EMAIL_PATTERN = /^[\w\-_.]+@/ def to_magic text # debug MAGIC_PATTERN.to_s text.gsub!(MAGIC_PATTERN) { magic = $2 $1 + case magic when IS_SMILEY_PATTERN Smileys.smiley_to_img magic when IS_URL_PATTERN last = magic.slice_punctation! # no punctation in my URL href = magic href.insert(0, 'http://') unless magic =~ URL_STARTS_WITH_PROTOCOL '' + magic + '' + last when IS_EMAIL_PATTERN last = magic.slice_punctation! '' + magic + '' + last else raise '{{{' + magic + '}}}' end } text end # handles smileys and urls def parse_magic html return html unless @do_magic scanner = StringScanner.new html out = '' while scanner.rest? if scanner.scan(/ < (?: a\s .*? <\/a> | pre\W .*? <\/pre> | [^>]* > ) /mx) out << scanner.matched elsif scanner.scan(/ [^<]+ /x) out << to_magic(scanner.matched) # this should never happen elsif scanner.scan(/./m) raise 'ERROR: else case reached' end end out end end # Parser end class String def slice_punctation! slice!(/[.:,!\?]+$/).to_s # return '' instead of nil end end # # = Grammar # # An implementation of common algorithms on grammars. # # This is used by Shinobu, a visualization tool for educating compiler-building. # # Thanks to Andreas Kunert for his wonderful LR(k) Pamphlet (German, see http://www.informatik.hu-berlin.de/~kunert/papers/lr-analyse), and Aho/Sethi/Ullman for their Dragon Book. # # Homepage:: http://shinobu.cYcnus.de (not existing yet) # Author:: murphy (Kornelius Kalnbach) # Copyright:: (cc) 2005 cYcnus # License:: GPL # Version:: 0.2.0 (2005-03-27) require 'set_hash' require 'ctype' require 'tools' require 'rules' require 'trace' require 'first' require 'follow' # = Grammar # # == Syntax # # === Rules # # Each line is a rule. # The syntax is # # left - right # # where +left+ and +right+ can be uppercase and lowercase letters, # and - can be any combination of <, >, - or whitespace. # # === Symbols # # Uppercase letters stand for meta symbols, lowercase for terminals. # # You can make epsilon-derivations by leaving empty. # # === Example # S - Ac # A - Sc # A - b # A - class Grammar attr_reader :tracer # Creates a new Grammar. # If $trace is true, the algorithms explain (textual) what they do to $stdout. def initialize data, tracer = Tracer.new @tracer = tracer @rules = Rules.new @terminals, @meta_symbols = SortedSet.new, Array.new @start_symbol = nil add_rules data end attr_reader :meta_symbols, :terminals, :rules, :start_symbol alias_method :sigma, :terminals alias_method :alphabet, :terminals alias_method :variables, :meta_symbols alias_method :nonterminals, :meta_symbols # A string representation of the grammar for debugging. def inspect productions_too = false 'Grammar(meta symbols: %s; alphabet: %s; productions: [%s]; start symbol: %s)' % [ meta_symbols.join(', '), terminals.join(', '), if productions_too @rules.inspect else @rules.size end, start_symbol ] end # Add rules to the grammar. +rules+ should be a String or respond to +scan+ in a similar way. # # Syntax: see Grammar. def add_rules grammar @rules = Rules.parse grammar do |rule| @start_symbol ||= rule.left @meta_symbols << rule.left @terminals.merge rule.right.split('').select { |s| terminal? s } end @meta_symbols.uniq! update end # Returns a hash acting as FIRST operator, so that # first["ABC"] is FIRST(ABC). # See http://en.wikipedia.org/wiki/LL_parser "Constructing an LL(1) parsing table" for details. def first first_operator end # Returns a hash acting as FOLLOW operator, so that # first["A"] is FOLLOW(A). # See http://en.wikipedia.org/wiki/LL_parser "Constructing an LL(1) parsing table" for details. def follow follow_operator end LLError = Class.new(Exception) LLErrorType1 = Class.new(LLError) LLErrorType2 = Class.new(LLError) # Tests if the grammar is LL(1). def ll1? begin for meta in @meta_symbols first_sets = @rules[meta].map { |alpha| first[alpha] } first_sets.inject(Set[]) do |already_used, another_first_set| unless already_used.disjoint? another_first_set raise LLErrorType1 end already_used.merge another_first_set end if first[meta].include? EPSILON and not first[meta].disjoint? follow[meta] raise LLErrorType2 end end rescue LLError false else true end end private def first_operator @first ||= FirstOperator.new self end def follow_operator @follow ||= FollowOperator.new self end def update @first = @follow = nil end end if $0 == __FILE__ eval DATA.read, nil, $0, __LINE__+4 end require 'test/unit' class TestCaseGrammar < Test::Unit::TestCase include Grammar::Symbols def fifo s Set[*s.split('')] end def test_fifo assert_equal Set[], fifo('') assert_equal Set[EPSILON, END_OF_INPUT, 'x', 'Y'], fifo('?xY$') end TEST_GRAMMAR_1 = <<-EOG S - ABCD A - a A - B - b B - C - c C - D - S D - EOG def test_symbols assert EPSILON assert END_OF_INPUT end def test_first_1 g = Grammar.new TEST_GRAMMAR_1 f = nil assert_nothing_raised { f = g.first } assert_equal(Set['a', EPSILON], f['A']) assert_equal(Set['b', EPSILON], f['B']) assert_equal(Set['c', EPSILON], f['C']) assert_equal(Set['a', 'b', 'c', EPSILON], f['D']) assert_equal(f['D'], f['S']) end def test_follow_1 g = Grammar.new TEST_GRAMMAR_1 f = nil assert_nothing_raised { f = g.follow } assert_equal(Set['a', 'b', 'c', END_OF_INPUT], f['A']) assert_equal(Set['a', 'b', 'c', END_OF_INPUT], f['B']) assert_equal(Set['a', 'b', 'c', END_OF_INPUT], f['C']) assert_equal(Set[END_OF_INPUT], f['D']) assert_equal(Set[END_OF_INPUT], f['S']) end TEST_GRAMMAR_2 = <<-EOG S - Ed E - EpT E - EmT E - T T - TuF T - TdF T - F F - i F - n F - aEz EOG def test_first_2 g = Grammar.new TEST_GRAMMAR_2 f = nil assert_nothing_raised { f = g.first } assert_equal(Set['a', 'n', 'i'], f['E']) assert_equal(Set['a', 'n', 'i'], f['F']) assert_equal(Set['a', 'n', 'i'], f['T']) assert_equal(Set['a', 'n', 'i'], f['S']) end def test_follow_2 g = Grammar.new TEST_GRAMMAR_2 f = nil assert_nothing_raised { f = g.follow } assert_equal(Set['m', 'd', 'z', 'p'], f['E']) assert_equal(Set['m', 'd', 'z', 'p', 'u'], f['F']) assert_equal(Set['m', 'd', 'z', 'p', 'u'], f['T']) assert_equal(Set[END_OF_INPUT], f['S']) end LLError = Grammar::LLError TEST_GRAMMAR_3 = <<-EOG E - TD D - pTD D - T - FS S - uFS S - S - p F - aEz F - i EOG NoError = Class.new(Exception) def test_first_3 g = Grammar.new TEST_GRAMMAR_3 # Grammar 3 is LL(1), so all first-sets must be disjoint. f = nil assert_nothing_raised { f = g.first } assert_equal(Set['a', 'i'], f['E']) assert_equal(Set[EPSILON, 'p'], f['D']) assert_equal(Set['a', 'i'], f['F']) assert_equal(Set['a', 'i'], f['T']) assert_equal(Set[EPSILON, 'u', 'p'], f['S']) for m in g.meta_symbols r = g.rules[m] firsts = r.map { |x| f[x] }.to_set assert_nothing_raised do firsts.inject(Set.new) do |already_used, another_first_set| raise LLError, 'not disjoint!' unless already_used.disjoint? another_first_set already_used.merge another_first_set end end end end def test_follow_3 g = Grammar.new TEST_GRAMMAR_3 # Grammar 3 is not LL(1), because epsilon is in FIRST(S), # but FIRST(S) and FOLLOW(S) are not disjoint. f = nil assert_nothing_raised { f = g.follow } assert_equal(Set['z', END_OF_INPUT], f['E']) assert_equal(Set['z', END_OF_INPUT], f['D']) assert_equal(Set['z', 'p', 'u', END_OF_INPUT], f['F']) assert_equal(Set['p', 'z', END_OF_INPUT], f['T']) assert_equal(Set['p', 'z', END_OF_INPUT], f['S']) for m in g.meta_symbols first_m = g.first[m] next unless first_m.include? EPSILON assert_raise(m == 'S' ? LLError : NoError) do if first_m.disjoint? f[m] raise NoError # this is fun :D else raise LLError end end end end TEST_GRAMMAR_3b = <<-EOG E - TD D - pTD D - PTD D - T - FS S - uFS S - F - aEz F - i P - p EOG def test_first_3b g = Grammar.new TEST_GRAMMAR_3b # Grammar 3b is NOT LL(1), since not all first-sets are disjoint. f = nil assert_nothing_raised { f = g.first } assert_equal(Set['a', 'i'], f['E']) assert_equal(Set[EPSILON, 'p'], f['D']) assert_equal(Set['p'], f['P']) assert_equal(Set['a', 'i'], f['F']) assert_equal(Set['a', 'i'], f['T']) assert_equal(Set[EPSILON, 'u'], f['S']) for m in g.meta_symbols r = g.rules[m] firsts = r.map { |x| f[x] } assert_raise(m == 'D' ? LLError : NoError) do firsts.inject(Set.new) do |already_used, another_first_set| raise LLError, 'not disjoint!' unless already_used.disjoint? another_first_set already_used.merge another_first_set end raise NoError end end end def test_follow_3b g = Grammar.new TEST_GRAMMAR_3b # Although Grammar 3b is NOT LL(1), the FOLLOW-condition is satisfied. f = nil assert_nothing_raised { f = g.follow } assert_equal(fifo('z$'), f['E'], 'E') assert_equal(fifo('z$'), f['D'], 'D') assert_equal(fifo('ai'), f['P'], 'P') assert_equal(fifo('z$pu'), f['F'], 'F') assert_equal(fifo('z$p'), f['T'], 'T') assert_equal(fifo('z$p'), f['S'], 'S') for m in g.meta_symbols first_m = g.first[m] next unless first_m.include? EPSILON assert_raise(NoError) do if first_m.disjoint? f[m] raise NoError # this is fun :D else raise LLError end end end end def test_ll1? assert_equal false, Grammar.new(TEST_GRAMMAR_3).ll1?, 'Grammar 3' assert_equal false, Grammar.new(TEST_GRAMMAR_3b).ll1?, 'Grammar 3b' end def test_new assert_nothing_raised { Grammar.new '' } assert_nothing_raised { Grammar.new TEST_GRAMMAR_1 } assert_nothing_raised { Grammar.new TEST_GRAMMAR_2 } assert_nothing_raised { Grammar.new TEST_GRAMMAR_3 } assert_nothing_raised { Grammar.new TEST_GRAMMAR_1 + TEST_GRAMMAR_2 + TEST_GRAMMAR_3 } assert_raise(ArgumentError) { Grammar.new 'S - ?' } end end # vim:foldmethod=syntax #!/usr/bin/env ruby require 'fox12' include Fox class Window < FXMainWindow def initialize(app) super(app, app.appName + ": First Set Calculation", nil, nil, DECOR_ALL, 0, 0, 800, 600, 0, 0) # {{{ menubar menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X) filemenu = FXMenuPane.new(self) FXMenuCommand.new(filemenu, "&Start\tCtl-S\tStart the application.", nil, getApp()).connect(SEL_COMMAND, method(:start)) FXMenuCommand.new(filemenu, "&Quit\tAlt-F4\tQuit the application.", nil, getApp(), FXApp::ID_QUIT) FXMenuTitle.new(menubar, "&File", nil, filemenu) # }}} menubar # {{{ statusbar @statusbar = FXStatusBar.new(self, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|STATUSBAR_WITH_DRAGCORNER) # }}} statusbar # {{{ window content horizontalsplitt = FXSplitter.new(self, SPLITTER_VERTICAL|LAYOUT_SIDE_TOP|LAYOUT_FILL) @productions = FXList.new(horizontalsplitt, nil, 0, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FIX_HEIGHT|LIST_SINGLESELECT) @productions.height = 100 @result = FXTable.new(horizontalsplitt, nil, 0, LAYOUT_FILL) @result.height = 200 @result.setTableSize(2, 2, false) @result.rowHeaderWidth = 0 header = @result.columnHeader header.setItemText 0, 'X' header.setItemText 1, 'FIRST(X)' for item in header item.justification = FXHeaderItem::CENTER_X end @debug = FXText.new(horizontalsplitt, nil, 0, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|LAYOUT_FIX_HEIGHT) @debug.height = 200 # }}} window content end def load_grammar grammar @tracer = FirstTracer.new(self) @grammar = Grammar.new grammar, @tracer @rules_indexes = Hash.new @grammar.rules.each_with_index do |rule, i| @productions.appendItem rule.inspect @rules_indexes[rule] = i end end def create super show(PLACEMENT_SCREEN) end def rule rule @productions.selectItem @rules_indexes[rule] sleep 0.1 end def iterate i setTitle i.to_s sleep 0.1 end def missing what @debug.appendText what + "\n" sleep 0.1 end def start sender, sel, pointer Thread.new do begin @grammar.first rescue => boom @debug.appendText [boom.to_s, *boom.backtrace].join("\n") end end end end $: << 'grammar' require 'grammar' require 'first_tracer' app = FXApp.new("Shinobu", "cYcnus") # fenster erzeugen window = Window.new app unless ARGV.empty? grammar = File.read(ARGV.first) else grammar = <<-EOG1 Z --> S S --> Sb S --> bAa A --> aSc A --> a A --> aSb EOG1 end window.load_grammar grammar app.create app.run require 'erb' require 'ftools' require 'yaml' require 'redcloth' module WhyTheLuckyStiff class Book attr_accessor :author, :title, :terms, :image, :teaser, :chapters, :expansion_paks, :encoding, :credits def [] x @lang.fetch(x) do warn warning = "[not translated: '#{x}'!]" warning end end end def Book::load( file_name ) YAML::load( File.open( file_name ) ) end class Section attr_accessor :index, :header, :content def initialize( i, h, c ) @index, @header, @content = i, h, RedCloth::new( c.to_s ) end end class Sidebar attr_accessor :title, :content end YAML::add_domain_type( 'whytheluckystiff.net,2003', 'sidebar' ) do |taguri, val| YAML::object_maker( Sidebar, 'title' => val.keys.first, 'content' => RedCloth::new( val.values.first ) ) end class Chapter attr_accessor :index, :title, :sections def initialize( i, t, sects ) @index = i @title = t i = 0 @sections = sects.collect do |s| if s.respond_to?( :keys ) i += 1 Section.new( i, s.keys.first, s.values.first ) else s end end end end YAML::add_domain_type( 'whytheluckystiff.net,2003', 'book' ) do |taguri, val| ['chapters', 'expansion_paks'].each do |chaptype| i = 0 val[chaptype].collect! do |c| i += 1 Chapter::new( i, c.keys.first, c.values.first ) end end val['teaser'].collect! do |t| Section::new( 1, t.keys.first, t.values.first ) end val['terms'] = RedCloth::new( val['terms'] ) YAML::object_maker( Book, val ) end class Image attr_accessor :file_name end YAML::add_domain_type( 'whytheluckystiff.net,2003', 'img' ) do |taguri, val| YAML::object_maker( Image, 'file_name' => "i/" + val ) end end # # Convert the book to HTML # if __FILE__ == $0 unless ARGV[0] puts "Usage: #{$0} [/path/to/save/html]" exit end site_path = ARGV[0] book = WhyTheLuckyStiff::Book::load( 'poignant.yml' ) chapter = nil # Write index page index_tpl = ERB::new( File.open( 'index.erb' ).read ) File.open( File.join( site_path, 'index.html' ), 'w' ) do |out| out << index_tpl.result end book.chapters = book.chapters[0,3] if ARGV.include? '-fast' # Write chapter pages chapter_tpl = ERB::new( File.open( 'chapter.erb' ).read ) book.chapters.each do |chapter| File.open( File.join( site_path, "chapter-#{ chapter.index }.html" ), 'w' ) do |out| out << chapter_tpl.result end end exit if ARGV.include? '-fast' # Write expansion pak pages expak_tpl = ERB::new( File.open( 'expansion-pak.erb' ).read ) book.expansion_paks.each do |pak| File.open( File.join( site_path, "expansion-pak-#{ pak.index }.html" ), 'w' ) do |out| out << expak_tpl.result( binding ) end end # Write printable version print_tpl = ERB::new( File.open( 'print.erb' ).read ) File.open( File.join( site_path, "print.html" ), 'w' ) do |out| out << print_tpl.result end # Copy css + images into site copy_list = ["guide.css"] + Dir["i/*"].find_all { |image| image =~ /\.(gif|jpg|png)$/ } File.makedirs( File.join( site_path, "i" ) ) copy_list.each do |copy_file| File.copy( copy_file, File.join( site_path, copy_file ) ) end end #!/usr/bin/env ruby require 'fox' begin require 'opengl' rescue LoadError require 'fox/missingdep' MSG = <(side) self.num <=> side.num end def init_facelet(pos, *side_nums) sides = side_nums.map { |num| @sides[num] }.sort @fl_by_side[sides] = pos end def []=(color, *sides) @facelets[@fl_by_side[sides.sort]].color = color end def values_at(*sides) sides.map { |sides| @facelets[@fl_by_side[sides.sort]] } end def inspect(range=nil) if range @facelets.values_at(*(range.to_a)).join(' ') else <<-EOS.gsub(/\d/) { |num| @facelets[num.to_i] }.gsub(/[ABCD]/) { |side| @sides[side[0]-?A].num.to_s } A 0 1 2 D 3 4 5 B 6 7 8 C EOS end end def get_edge(side) trio = (-1..1).map { |x| (side + x) % 4 } prev_side, this_side, next_side = @sides.values_at(*trio) e = Edge.new( self .values_at( [this_side], [this_side, next_side] ) + this_side.values_at( [self, prev_side], [self ], [self, next_side] ) ) #puts 'Edge created for side %d: ' % side + e.inspect e end def turn(dir) #p 'turn side %d in %d' % [num, dir] edges = (0..3).map { |n| get_edge n } for i in 0..3 edges[i].apply edges[(i-dir) % 4] end end end class Cube def initialize @sides = [] %w(left front right back top bottom).each_with_index { |side, i| eval("@sides[#{i}] = @#{side} = Side.new(#{i})") } @left.sides = [@top, @front, @bottom, @back] @front.sides = [@top, @right, @bottom, @left] @right.sides = [@top, @back, @bottom, @front] @back.sides = [@top, @left, @bottom, @right] @top.sides = [@back, @right, @front, @left] @bottom.sides = [@front, @right, @back, @left] end def read_facelets(fs) pattern = Regexp.new(<<-EOP.gsub(/\w/, '\w').gsub(/\s+/, '\s*')) (w w w) (w w w) (w w w) (r r r) (g g g) (b b b) (o o o) (r r r) (g g g) (b b b) (o o o) (r r r) (g g g) (b b b) (o o o) (y y y) (y y y) (y y y) EOP md = pattern.match(fs).to_a @top.facelets = parse_facelets(md.values_at(1,2,3)) @left.facelets = parse_facelets(md.values_at(4,8,12)) @front.facelets = parse_facelets(md.values_at(5,9,13)) @right.facelets = parse_facelets(md.values_at(6,10,14)) @back.facelets = parse_facelets(md.values_at(7,11,15)) @bottom.facelets = parse_facelets(md.values_at(16,17,18)) end def turn(side, dir) #p 'turn %d in %d' % [side, dir] @sides[side].turn(dir) #puts inspect end def inspect <<-EOF.gsub(/(\d):(\d)-(\d)/) { @sides[$1.to_i].inspect(Range.new($2.to_i, $3.to_i)) } 4:0-2 4:3-5 4:6-8 0:0-2 1:0-2 2:0-2 3:0-2 0:3-5 1:3-5 2:3-5 3:3-5 0:6-8 1:6-8 2:6-8 3:6-8 5:0-2 5:3-5 5:6-8 EOF end private def parse_facelets(rows) rows.join.delete(' ').split(//) end end #$stdin = DATA gets.to_i.times do |i| puts "Scenario ##{i+1}:" fs = '' 9.times { fs << gets } cube = Cube.new cube.read_facelets fs gets.to_i.times do |t| side, dir = gets.split.map {|s| s.to_i} cube.turn(side, dir) end puts cube.inspect puts end # 2004 by murphy # GPL class Scenario class TimePoint attr_reader :data def initialize *data @data = data end def [] i @data[i] or 0 end include Comparable def <=> tp r = 0 [@data.size, tp.data.size].max.times do |i| r = self[i] <=> tp[i] return r if r.nonzero? end 0 end def - tp r = [] [@data.size, tp.data.size].max.times do |i| r << self[i] - tp[i] end r end def inspect # 01/01/1800 00:00:00 '%02d/%02d/%04d %02d:%02d:%02d' % @data.values_at(1, 2, 0, 3, 4, 5) end end ONE_HOUR = TimePoint.new 0, 0, 0, 1, 0, 0 APPOINTMENT_PATTERN = / ( \d{4} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{4} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) /x def initialize io @team_size = io.gets.to_i @data = [ [TimePoint.new(1800, 01, 01, 00, 00, 00), @team_size] ] @team_size.times do # each team member io.gets.to_i.times do # each appointment m = APPOINTMENT_PATTERN.match io.gets @data << [TimePoint.new(*m.captures[0,6].map { |x| x.to_i }), -1] @data << [TimePoint.new(*m.captures[6,6].map { |x| x.to_i }), +1] end end @data << [TimePoint.new(2200, 01, 01, 00, 00, 00), -@team_size] end def print_time_plan n = 0 appointment = nil no_appointment = true @data.sort_by { |x| x[0] }.each do |x| tp, action = *x n += action # at any time during the meeting, at least two team members need to be there # and at most one team member is allowed to be absent if n >= 2 and (@team_size - n) <= 1 appointment ||= tp else if appointment # the meeting should be at least one hour in length if TimePoint.new(*(tp - appointment)) >= ONE_HOUR puts 'appointment possible from %p to %p' % [appointment, tp] no_appointment = false end appointment = false end end end puts 'no appointment possible' if no_appointment end end # read the data DATA.gets.to_i.times do |si| # each scenario puts 'Scenario #%d:' % (si + 1) sc = Scenario.new DATA sc.print_time_plan puts end #__END__ 2 3 3 2002 06 28 15 00 00 2002 06 28 18 00 00 TUD Contest Practice Session 2002 06 29 10 00 00 2002 06 29 15 00 00 TUD Contest 2002 11 15 15 00 00 2002 11 17 23 00 00 NWERC Delft 4 2002 06 25 13 30 00 2002 06 25 15 30 00 FIFA World Cup Semifinal I 2002 06 26 13 30 00 2002 06 26 15 30 00 FIFA World Cup Semifinal II 2002 06 29 13 00 00 2002 06 29 15 00 00 FIFA World Cup Third Place 2002 06 30 13 00 00 2002 06 30 15 00 00 FIFA World Cup Final 1 2002 06 01 00 00 00 2002 06 29 18 00 00 Preparation of Problem Set 2 1 1800 01 01 00 00 00 2200 01 01 00 00 00 Solving Problem 8 0 require 'token_consts' require 'symbol' require 'ctype' require 'error' class Fixnum # Treat char as a digit and return it's value as Fixnum. # Returns nonsense for non-digits. # Examples: # # RUBY_VERSION[0].digit == '1.8.2'[0].digit == 1 # # # # ?6.digit == 6 # # # # ?A.digit == 17 # def digit self - ?0 end end ## # Stellt einen einfachen Scanner für die lexikalische Analyse der Sprache Pas-0 dar. # # @author Andreas Kunert # Ruby port by murphy class Scanner include TokenConsts attr_reader :line, :pos # To allow Scanner.new without parameters. DUMMY_INPUT = 'dummy file' def DUMMY_INPUT.getc nil end ## # Erzeugt einen Scanner, der als Eingabe das übergebene IO benutzt. def initialize input = DUMMY_INPUT @line = 1 @pos = 0 begin @input = input @next_char = @input.getc rescue IOError # TODO show the reason! Error.ioError raise end end ## # Liest das nchste Zeichen von der Eingabe. def read_next_char begin @pos += 1 @current_char = @next_char @next_char = @input.getc rescue IOError Error.ioError raise end end ## # Sucht das nächste Symbol, identifiziert es, instantiiert ein entsprechendes # PascalSymbol-Objekt und gibt es zurück. # @see Symbol # @return das gefundene Symbol als PascalSymbol-Objekt def get_symbol current_symbol = nil until current_symbol read_next_char if @current_char.alpha? identifier = @current_char.chr while @next_char.alpha? or @next_char.digit? identifier << @next_char read_next_char end current_symbol = handle_identifier(identifier.upcase) elsif @current_char.digit? current_symbol = number else case @current_char when ?\s # ignore when ?\n new_line when nil current_symbol = PascalSymbol.new EOP when ?{ comment when ?: if @next_char == ?= read_next_char current_symbol = PascalSymbol.new BECOMES else current_symbol = PascalSymbol.new COLON end when ?< if (@next_char == ?=) read_next_char current_symbol = PascalSymbol.new LEQSY elsif (@next_char == ?>) read_next_char current_symbol = PascalSymbol.new NEQSY else current_symbol = PascalSymbol.new LSSSY end when ?> if (@next_char == ?=) read_next_char current_symbol = PascalSymbol.new GEQSY else current_symbol = PascalSymbol.new GRTSY end when ?. then current_symbol = PascalSymbol.new PERIOD when ?( then current_symbol = PascalSymbol.new LPARENT when ?, then current_symbol = PascalSymbol.new COMMA when ?* then current_symbol = PascalSymbol.new TIMES when ?/ then current_symbol = PascalSymbol.new SLASH when ?+ then current_symbol = PascalSymbol.new PLUS when ?- then current_symbol = PascalSymbol.new MINUS when ?= then current_symbol = PascalSymbol.new EQLSY when ?) then current_symbol = PascalSymbol.new RPARENT when ?; then current_symbol = PascalSymbol.new SEMICOLON else Error.error(100, @line, @pos) if @current_char > ?\s end end end current_symbol end private ## # Versucht, in dem gegebenen String ein Schlüsselwort zu erkennen. # Sollte dabei ein Keyword gefunden werden, so gibt er ein PascalSymbol-Objekt zurück, das # das entsprechende Keyword repräsentiert. Ansonsten besteht die Rückgabe aus # einem SymbolIdent-Objekt (abgeleitet von PascalSymbol), das den String 1:1 enthält # @see symbol # @return falls Keyword gefunden, zugehöriges PascalSymbol, sonst SymbolIdent def handle_identifier identifier if sym = KEYWORD_SYMBOLS[identifier] PascalSymbol.new sym else SymbolIdent.new identifier end end MAXINT = 2**31 - 1 MAXINT_DIV_10 = MAXINT / 10 MAXINT_MOD_10 = MAXINT % 10 ## # Versucht, aus dem gegebenen Zeichen und den folgenden eine Zahl zusammenzusetzen. # Dabei wird der relativ intuitive Algorithmus benutzt, die endgültige Zahl bei # jeder weiteren Ziffer mit 10 zu multiplizieren und diese dann mit der Ziffer zu # addieren. Sonderfälle bestehen dann nur noch in der Behandlung von reellen Zahlen. #
# Treten dabei kein Punkt oder ein E auf, so gibt diese Methode ein SymbolIntCon-Objekt # zurück, ansonsten (reelle Zahl) ein SymbolRealCon-Objekt. Beide Symbole enthalten # jeweils die Zahlwerte. #
# Anmerkung: Diese Funktion ist mit Hilfe der Java/Ruby-API deutlich leichter zu realisieren. # Sie wurde dennoch so implementiert, um den Algorithmus zu demonstrieren # @see symbol # @return SymbolIntcon- oder SymbolRealcon-Objekt, das den Zahlwert enthält def number is_integer = true integer_too_long = false exponent = 0 exp_counter = -1 exp_sign = 1 integer_mantisse = @current_char.digit while (@next_char.digit? and integer_mantisse < MAXINT_DIV_10) or (integer_mantisse == MAXINT_DIV_10 and @next_char.digit <= MAXINT_MOD_10) integer_mantisse *= 10 integer_mantisse += @next_char.digit read_next_char end real_mantisse = integer_mantisse while @next_char.digit? integer_too_long = true real_mantisse *= 10 real_mantisse += @next_char.digit read_next_char end if @next_char == ?. read_next_char is_integer = false unless @next_char.digit? Error.error 101, @line, @pos end while @next_char.digit? real_mantisse += @next_char.digit * (10 ** exp_counter) read_next_char exp_counter -= 1 end end if @next_char == ?E is_integer = false read_next_char if @next_char == ?- exp_sign = -1 read_next_char end unless @next_char.digit? Error.error 101, @line, @pos end while @next_char.digit? exponent *= 10 exponent += @next_char.digit read_next_char end end if is_integer if integer_too_long Error.error 102, @line, @pos end SymbolIntcon.new integer_mantisse else SymbolRealcon.new real_mantisse * (10 ** (exp_sign * exponent)) end end ## # Sorgt für ein Überlesen von Kommentaren. # Es werden einfach alle Zeichen bis zu einer schließenden Klammer eingelesen # und verworfen. def comment while @current_char != ?} forbid_eop new_line if @current_char == ?\n read_next_char end end def new_line @line += 1 @pos = 0 end def forbid_eop if eop? Error.error 103, @line, @pos end exit end def eop? @current_char.nil? end end ## # Läßt ein Testprogramm ablaufen. # Dieses erzeugt sich ein Scanner-Objekt und ruft an diesem kontinuierlich bis zum Dateiende # get_symbol auf. if $0 == __FILE__ scan = Scanner.new(File.new(ARGV[0] || 'test.pas')) loop do c = scan.get_symbol puts c break if c.typ == TokenConsts::EOP end end # -*- 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(a) a.size == 0 ? '' : a.size == 1 ? a[0] : a.size == 2 ? a.join(' and ') : a[0..-2].join(', ') + ', and ' + a[-1] 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 def commify_series(a) sepchar = a.select { |p| p =~ /,/ } != [] ? '; ' : ', ' a.size == 0 ? '' : a.size == 1 ? a[0] : a.size == 2 ? a.join(' and ') : a[0..-2].join(sepchar) + sepchar + 'and ' + a[-1] 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 # @@PLEAC@@_4.18 #!/usr/bin/env ruby # example 4-2 words # words - gather lines, present in colums # class to encapsulate the word formatting from the input class WordFormatter def initialize(cols) @cols = cols end # helper to return the length of the longest word in the wordlist def maxlen(wordlist) max = 1 for word in wordlist if word.length > max max = word.length end end max end # process the wordlist and print it formmated into columns def output(wordlist) collen = maxlen(wordlist) + 1 columns = @cols / collen columns = 1 if columns == 0 rows = (wordlist.length + columns - 1) / columns # now process each item, picking out proper piece for this position 0.upto(rows * columns - 1) { |item| target = (item % columns) * rows + (item / columns) eol = ((item+1) % columns == 0) piece = wordlist[target] || "" piece = piece.ljust(collen) unless eol print piece puts if eol } # no need to finish it up, because eol is always true for the last element end end # get nr of chars that fit in window or console, see PLEAC 15.4 # not portable -- linux only (?) def getWinCharWidth() buf = "\0" * 8 $stdout.ioctl(0x5413, buf) ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("$4") ws_col || 80 rescue 80 end # main program cols = getWinCharWidth() formatter = WordFormatter.new(cols) words = readlines() words.collect! { |line| line.chomp } formatter.output(words) # @@PLEAC@@_4.19 # In ruby, Fixnum's are automatically converted to Bignum's when # needed, so there is no need for an extra module def factorial(n) s = 1 while n > 0 s *= n n -= 1 end s end puts factorial(500) #--------------------------------------------------------- # Example 4-3. tsc-permute # tsc_permute: permute each word of input def permute(items, perms) unless items.length > 0 puts perms.join(" ") else for i in items newitems = items.dup newperms = perms.dup newperms.unshift(newitems.delete(i)) permute(newitems, newperms) end end end # In ruby the main program must be after all definitions it is using permute(ARGV, []) #--------------------------------------------------------- # mjd_permute: permute each word of input def factorial(n) s = 1 while n > 0 s *= n n -= 1 end s end # we use a class with a class variable store the private cache # for the results of the factorial function. class Factorial @@fact = [ 1 ] def Factorial.compute(n) if @@fact[n] @@fact[n] else @@fact[n] = n * Factorial.compute(n - 1) end end end #--------------------------------------------------------- # Example 4-4- mjd-permute # n2pat(n, len): produce the N-th pattern of length len # We must use a lower case letter as parameter N, otherwise it is # handled as constant Length is the length of the resulting # array, not the index of the last element (length -1) like in # the perl example. def n2pat(n, length) pat = [] i = 1 while i <= length pat.push(n % i) n /= i i += 1 end pat end # pat2perm(pat): turn pattern returned by n2pat() into # permutation of integers. def pat2perm(pat) source = (0 .. pat.length - 1).to_a perm = [] perm.push(source.slice!(pat.pop)) while pat.length > 0 perm end def n2perm(n, len) pat2perm(n2pat(n,len)) end # In ruby the main program must be after all definitions while gets data = split # the perl solution has used $#data, which is length-1 num_permutations = Factorial.compute(data.length()) 0.upto(num_permutations - 1) do |i| # in ruby we can not use an array as selector for an array # but by exchanging the two arrays, we can use the collect method # which returns an array with the result of all block invocations permutation = n2perm(i, data.length).collect { |j| data[j] } puts permutation.join(" ") end end # @@PLEAC@@_5.0 age = { "Nat", 24, "Jules", 25, "Josh", 17 } age["Nat"] = 24 age["Jules"] = 25 age["Josh"] = 17 food_color = { "Apple" => "red", "Banana" => "yellow", "Lemon" => "yellow", "Carrot" => "orange" } # In Ruby, you cannot avoid the double or simple quoting # while manipulatin hashes # @@PLEAC@@_5.1 hash[key] = value food_color["Raspberry"] = "pink" puts "Known foods:", food_color.keys # @@PLEAC@@_5.2 # does hash have a value for key ? if (hash.has_key?(key)) # it exists else # it doesn't end [ "Banana", "Martini" ].each { |name| print name, " is a ", food_color.has_key?(name) ? "food" : "drink", "\n" } age = {} age['Toddler'] = 3 age['Unborn'] = 0 age['Phantasm'] = nil for thing in ['Toddler', 'Unborn', 'Phantasm', 'Relic'] print "#{thing}: " print "Has-key " if age.has_key?(thing) print "True " if age[thing] print "Nonzero " if age[thing] && age[thing].nonzero? print "\n" end #=> # Toddler: Has-key True Nonzero # Unborn: Has-key True # Phantasm: Has-key # Relic: # You use Hash#has_key? when you use Perl's exists -> it checks # for existence of a key in a hash. # All Numeric are "True" in ruby, so the test doesn't have the # same semantics as in Perl; you would use Numeric#nonzero? to # achieve the same semantics (false if 0, true otherwise). # @@PLEAC@@_5.3 food_color.delete("Banana") # @@PLEAC@@_5.4 hash.each { |key, value| # do something with key and value } hash.each_key { |key| # do something with key } food_color.each { |food, color| puts "#{food} is #{color}" } food_color.each_key { |food| puts "#{food} is #{food_color[food]}" } # IMO this demonstrates that OO style is by far more readable food_color.keys.sort.each { |food| puts "#{food} is #{food_color[food]}." } #----------------------------- #!/usr/bin/ruby # countfrom - count number of messages from each sender # Default value is 0 from = Hash.new(0) while gets /^From: (.*)/ and from[$1] += 1 end # More useful to sort by number of received mail by person from.sort {|a,b| b[1]<=>a[1]}.each { |v| puts "#{v[1]}: #{v[0]}" } #----------------------------- # @@PLEAC@@_5.5 # You may use the built-in 'inspect' method this way: p hash # Or do it the Cookbook way: hash.each { |k,v| puts "#{k} => #{v}" } # Sorted by keys hash.sort.each { |e| puts "#{e[0]} => #{e[1]}" } # Sorted by values hash.sort{|a,b| a[1]<=>b[1]}.each { |e| puts "#{e[0]} => #{e[1]}" } # @@PLEAC@@_5.7 ttys = Hash.new for i in `who` user, tty = i.split (ttys[user] ||= []) << tty # see problems_ruby for more infos end ttys.keys.sort.each { |k| puts "#{k}: #{commify_series(ttys[k])}" # from 4.2 } # @@PLEAC@@_5.8 surname = { "Mickey" => "Mantle", "Babe" => "Ruth" } puts surname.index("Mantle") # If you really needed to 'invert' the whole hash, use Hash#invert #----------------------------- #!/usr/bin/ruby -w # foodfind - find match for food or color given = ARGV.shift or raise "usage: foodfind food_or_color" color = { "Apple" => "red", "Banana" => "yellow", "Lemon" => "yellow", "Carrot" => "orange", } if (color.has_key?(given)) puts "#{given} is a food with color #{color[given]}." end if (color.has_value?(given)) puts "#{color.index(given)} is a food with color #{given}." end #----------------------------- # @@PLEAC@@_5.9 # Sorted by keys (Hash#sort gives an Array of pairs made of each key,value) food_color.sort.each { |f| puts "#{f[0]} is #{f[1]}." } # Sorted by values food_color.sort { |a,b| a[1] <=> b[1] }.each { |f| puts "#{f[0]} is #{f[1]}." } # Sorted by length of values food_color.sort { |a,b| a[1].length <=> b[1].length }.each { |f| puts "#{f[0]} is #{f[1]}." } # @@PLEAC@@_5.10 merged = a.clone.update(b) # because Hash#update changes object in place drink_color = { "Galliano" => "yellow", "Mai Tai" => "blue" } ingested_color = drink_color.clone.update(food_color) substance_color = {} for i in [ food_color, drink_color ] i.each_key { |k| if substance_color.has_key?(k) puts "Warning: #{k} seen twice. Using the first definition." next end substance_color[k] = 1 } end # @@PLEAC@@_5.11 common = hash1.keys & hash2.keys this_not_that = hash1.keys - hash2.keys # @@PLEAC@@_5.12 # no problem here, Ruby handles any kind of object for key-ing # (it takes Object#hash, which defaults to Object#id) # @@PLEAC@@_5.13 # AFAIK, not possible in Ruby # @@PLEAC@@_5.14 # Be careful, the following is possible only because Fixnum objects are # special (documentation says: there is effectively only one Fixnum object # instance for any given integer value). count = Hash.new(0) array.each { |e| count[e] += 1 } # @@PLEAC@@_5.15 father = { "Cain" , "Adam", "Abel" , "Adam", "Seth" , "Adam", "Enoch" , "Cain", "Irad" , "Enoch", "Mehujael" , "Irad", "Methusael" , "Mehujael", "Lamech" , "Methusael", "Jabal" , "Lamech", "Jubal" , "Lamech", "Tubalcain" , "Lamech", "Enos" , "Seth", } while gets chomp begin print $_, " " end while $_ = father[$_] puts end children = {} father.each { |k,v| (children[v] ||= []) << k } while gets chomp puts "#{$_} begat #{(children[$_] || ['Nobody']).join(', ')}.\n" end includes = {} files.each { |f| begin for l in IO.readlines(f) next unless l =~ /^\s*#\s*include\s*<([^>]+)>/ (includes[$1] ||= []) << f end rescue SystemCallError $stderr.puts "#$! (skipping)" end } include_free = includes.values.flatten.uniq - includes.keys # @@PLEAC@@_5.16 # dutree - print sorted intented rendition of du output #% dutree #% dutree /usr #% dutree -a #% dutree -a /bin # The DuNode class collects all information about a directory, # and provides some convenience methods class DuNode attr_reader :name attr_accessor :size attr_accessor :kids def initialize(name) @name = name @kids = [] @size = 0 end # support for sorting nodes with side def size_compare(node2) @size <=> node2.size end def basename @name.sub(/.*\//, "") end #returns substring before last "/", nil if not there def parent p = @name.sub(/\/[^\/]+$/,"") if p == @name nil else p end end end # The DuTree does the acdtual work of # getting the input, parsing it, builging up a tree # and format it for output class Dutree attr_reader :topdir def initialize @nodes = Hash.new @dirsizes = Hash.new(0) @kids = Hash.new([]) end # get a node by name, create it if it does not exist yet def get_create_node(name) if @nodes.has_key?(name) @nodes[name] else node = DuNode.new(name) @nodes[name] = node node end end # run du, read in input, save sizes and kids # stores last directory read in instance variable topdir def input(arguments) name = "" cmd = "du " + arguments.join(" ") IO.popen(cmd) { |pipe| pipe.each { |line| size, name = line.chomp.split(/\s+/, 2) node = get_create_node(name) node.size = size.to_i @nodes[name] = node parent = node.parent if parent get_create_node(parent).kids.push(node) end } } @topdir = @nodes[name] end # figure out how much is taken in each directory # that isn't stored in the subdirectories. Add a new # fake kid called "." containing that much. def get_dots(node) cursize = node.size for kid in node.kids cursize -= kid.size get_dots(kid) end if node.size != cursize newnode = get_create_node(node.name + "/.") newnode.size = cursize node.kids.push(newnode) end end # recursively output everything # passing padding and number width as well # on recursive calls def output(node, prefix="", width=0) line = sprintf("%#{width}d %s", node.size, node.basename) puts(prefix + line) prefix += line.sub(/\d /, "| ") prefix.gsub!(/[^|]/, " ") if node.kids.length > 0 # not a bachelor node kids = node.kids kids.sort! { |a,b| b.size_compare(a) } width = kids[0].size.to_s.length for kid in kids output(kid, prefix, width) end end end end tree = Dutree.new tree.input(ARGV) tree.get_dots(tree.topdir) tree.output(tree.topdir) # @@PLEAC@@_6.0 # The verbose version are match, sub, gsub, sub! and gsub!; # pattern needs to be a Regexp object; it yields a MatchData # object. pattern.match(string) string.sub(pattern, replacement) string.gsub(pattern, replacement) # As usual in Ruby, sub! does the same as sub but also modifies # the object, the same for gsub!/gsub. # Sugared syntax yields the position of the match (or nil if no # match). Note that the object at the right of the operator needs # not to be a Regexp object (it can be a String). The "dont # match" operator yields true or false. meadow =~ /sheep/ # position of the match, nil if no match meadow !~ /sheep/ # true if doesn't match, false if it does # There is no sugared version for the substitution meadow =~ /\bovines?\b/i and print "Here be sheep!" string = "good food" string.sub!(/o*/, 'e') # % echo ababacaca | ruby -ne 'puts $& if /(a|ba|b)+(a|ac)+/' # ababa # The "global" (or "multiple") match is handled by String#scan scan (/(\d+)/) { puts "Found number #{$1}" } # String#scan yields an Array if not used with a block numbers = scan(/\d+/) digits = "123456789" nonlap = digits.scan(/(\d\d\d)/) yeslap = digits.scan(/(?=(\d\d\d))/) puts "Non-overlapping: #{nonlap.join(' ')}" puts "Overlapping: #{yeslap.join(' ')}"; # Non-overlapping: 123 456 789 # Overlapping: 123 234 345 456 567 678 789 string = "And little lambs eat ivy" string =~ /l[^s]*s/ puts "(#$`) (#$&) (#$')" # (And ) (little lambs) ( eat ivy) # @@PLEAC@@_6.1 # Ruby doesn't have the same problem: dst = src.sub('this', 'that') progname = $0.sub('^.*/', '') bindirs = %w(/usr/bin /bin /usr/local/bin) libdirs = bindirs.map { |l| l.sub('bin', 'lib') } # @@PLEAC@@_6.3 /\S+/ # as many non-whitespace bytes as possible /[A-Za-z'-]+/ # as many letters, apostrophes, and hyphens /\b([A-Za-z]+)\b/ # usually best /\s([A-Za-z]+)\s/ # fails at ends or w/ punctuation # @@PLEAC@@_6.4 require 'socket' str = 'www.ruby-lang.org and www.rubygarden.org' re = / ( # capture the hostname in $1 (?: # these parens for grouping only (?! [-_] ) # lookahead for neither underscore nor dash [\w-] + # hostname component \. # and the domain dot ) + # now repeat that whole thing a bunch of times [A-Za-z] # next must be a letter [\w-] + # now trailing domain part ) # end of $1 capture /x # /x for nice formatting str.gsub! re do # pass a block to execute replacement host = TCPsocket.gethostbyname($1) "#{$1} [#{host[3]}]" end puts str #----------------------------- # to match whitespace or #-characters in an extended re you need to escape # them. foo = 42 str = 'blah #foo# blah' str.gsub! %r/ # replace \# # a pound sign (\w+) # the variable name \# # another pound sign /x do eval $1 # with the value of a local variable end puts str # => blah 42 blah # @@PLEAC@@_6.5 # The 'g' modifier doesn't exist in Ruby, a regexp can't be used # directly in a while loop; instead, use String#scan { |match| .. } fish = 'One fish two fish red fish blue fish' WANT = 3 count = 0 fish.scan(/(\w+)\s+fish\b/i) { if (count += 1) == WANT puts "The third fish is a #{$1} one." end } if fish =~ /(?:\w+\s+fish\s+){2}(\w+)\s+fish/i puts "The third fish is a #{$1} one." end pond = 'One fish two fish red fish blue fish' # String#scan without a block gives an array of matches, each match # being an array of all the specified groups colors = pond.scan(/(\w+)\s+fish\b/i).flatten # get all matches color = colors[2] # then the one we want # or without a temporary array color = pond.scan(/(\w+)\s+fish\b/i).flatten[2] # just grab element 3 puts "The third fish in the pond is #{color}." count = 0 fishes = 'One fish two fish red fish blue fish' evens = fishes.scan(/(\w+)\s+fish\b/i).select { (count+=1) % 2 == 0 } print "Even numbered fish are #{evens.join(' ')}." count = 0 fishes.gsub(/ \b # makes next \w more efficient ( \w+ ) # this is what we\'ll be changing ( \s+ fish \b ) /x) { if (count += 1) == 4 'sushi' + $2 else $1 + $2 end } pond = 'One fish two fish red fish blue fish swim here.' puts "Last fish is #{pond.scan(/\b(\w+)\s+fish\b/i).flatten[-1]}" / A # find some pattern A (?! # mustn\'t be able to find .* # something A # and A ) $ # through the end of the string /x # The "s" perl modifier is "m" in Ruby (not very nice since there is # also an "m" in perl..) pond = "One fish two fish red fish blue fish swim here." if (pond =~ / \b ( \w+) \s+ fish \b (?! .* \b fish \b ) /mix) puts "Last fish is #{$1}." else puts "Failed!" end # @@PLEAC@@_6.6 #----------------------------- #!/usr/bin/ruby -w # killtags - very bad html killer $/ = nil; # each read is whole file while file = gets() do file.gsub!(/<.*?>/m,''); # strip tags (terribly) puts file # print file to STDOUT end #----------------------------- #!/usr/bin/ruby -w #headerfy - change certain chapter headers to html $/ = '' while file = gets() do pattern = / \A # start of record ( # capture in $1 Chapter # text string \s+ # mandatory whitespace \d+ # decimal number \s* # optional whitespace : # a real colon . * # anything not a newline till end of line ) /x puts file.gsub(pattern,'

\1

') end #----------------------------- #% ruby -00pe "gsub!(/\A(Chapter\s+\d+\s*:.*)/,'

\1

')" datafile #!/usr/bin/ruby -w #----------------------------- for file in ARGV file = File.open(ARGV.shift) while file.gets('') do # each read is a paragraph print "chunk #{$.} in $ARGV has <<#{$1}>>\n" while /^START(.*?)^END/m end # /m activates the multiline mode end #----------------------------- # @@PLEAC@@_6.7 #----------------------------- $/ = nil; file = File.open("datafile") chunks = file.gets.split(/pattern/) #----------------------------- # .Ch, .Se and .Ss divide chunks of STDIN chunks = gets(nil).split(/^\.(Ch|Se|Ss)$/) print "I read #{chunks.size} chunks.\n" #----------------------------- # @@PLEAC@@_6.8 while gets if ~/BEGIN/ .. ~/END/ # line falls between BEGIN and END inclusive end end while gets if ($. == firstnum) .. ($. == lastnum) # operate between firstnum and lastnum line number end end # in ruby versions prior to 1.8, the above two conditional # expressions could be shortened to: # if /BEGIN/ .. /END/ # and # if firstnum .. lastnum # but these now only work this way from the command line #----------------------------- while gets if ~/BEGIN/ ... ~/END/ # line falls between BEGIN and END on different lines end end while gets if ($. == first) ... ($. == last) # operate between first and last line number on different lines end end #----------------------------- # command-line to print lines 15 through 17 inclusive (see below) ruby -ne 'print if 15 .. 17' datafile # print out all .. displays from HTML doc while gets print if ~%r##i .. ~%r##i; end # same, but as shell command # ruby -ne 'print if %r##i .. %r##i' document.html #----------------------------- # ruby -ne 'BEGIN { $top=3; $bottom=5 }; \ # print if $top .. $bottom' /etc/passwd # FAILS # ruby -ne 'BEGIN { $top=3; $bottom=5 }; \ # print if $. == $top .. $. == $bottom' /etc/passwd # works # ruby -ne 'print if 3 .. 5' /etc/passwd # also works #----------------------------- print if ~/begin/ .. ~/end/; print if ~/begin/ ... ~/end/; #----------------------------- while gets $in_header = $. == 1 .. ~/^$/ ? true : false $in_body = ~/^$/ .. ARGF.eof ? true : false end #----------------------------- seen = {} ARGF.each do |line| next unless line =~ /^From:?\s/i .. line =~ /^$/; line.scan(%r/([^<>(),;\s]+\@[^<>(),;\s]+)/).each do |addr| puts addr unless seen[addr] seen[addr] ||= 1 end end # @@PLEAC@@_6.9 def glob2pat(globstr) patmap = { '*' => '.*', '?' => '.', '[' => '[', ']' => ']', } globstr.gsub!(/(.)/) { |c| patmap[c] || Regexp::escape(c) } '^' + globstr + '$' end # @@PLEAC@@_6.10 # avoid interpolating patterns like this if the pattern # isn't going to change: pattern = ARGV.shift ARGF.each do |line| print line if line =~ /#{pattern}/ end # the above creates a new regex each iteration. Instead, # use the /o modifier so the regex is compiled only once pattern = ARGV.shift ARGF.each do |line| print line if line =~ /#{pattern}/o end #----------------------------- #!/usr/bin/ruby # popgrep1 - grep for abbreviations of places that say "pop" # version 1: slow but obvious way popstates = %w(CO ON MI WI MN) ARGF.each do |line| popstates.each do |state| if line =~ /\b#{state}\b/ print line last end end end #----------------------------- #!/usr/bin/ruby # popgrep2 - grep for abbreviations of places that say "pop" # version 2: eval strings; fast but hard to quote popstates = %w(CO ON MI WI MN) code = "ARGF.each do |line|\n" popstates.each do |state| code += "\tif line =~ /\\b#{state}\\b/; print(line); next; end\n" end code += "end\n" print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging eval code # CODE IS # --- # ARGF.each do |line| # if line =~ /\bCO\b/; print(line); next; end # if line =~ /\bON\b/; print(line); next; end # if line =~ /\bMI\b/; print(line); next; end # if line =~ /\bWI\b/; print(line); next; end # if line =~ /\bMN\b/; print(line); next; end # end # # --- ## alternatively, the same idea as above but compiling ## to a case statement: (not in perlcookbook) #!/usr/bin/ruby -w # popgrep2.5 - grep for abbreviations of places that say "pop" # version 2.5: eval strings; fast but hard to quote popstates = %w(CO ON MI WI MN) code = "ARGF.each do |line|\n case line\n" popstates.each do |state| code += " when /\\b#{state}\\b/ : print line\n" end code += " end\nend\n" print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging eval code # CODE IS # --- # ARGF.each do |line| # case line # when /\bCO\b/ : print line # when /\bON\b/ : print line # when /\bMI\b/ : print line # when /\bWI\b/ : print line # when /\bMN\b/ : print line # end # end # # --- # Note: (above) Ruby 1.8+ allows the 'when EXP : EXPR' on one line # with the colon separator. #----------------------------- #!/usr/bin/ruby # popgrep3 - grep for abbreviations of places that say "pop" # version3: build a match_any function popstates = %w(CO ON MI WI MN) expr = popstates.map{|e|"line =~ /\\b#{e}\\b/"}.join('||') eval "def match_any(line); #{expr};end" ARGF.each do |line| print line if match_any(line) end #----------------------------- ## building a match_all function is a trivial ## substitution of && for || ## here is a generalized example: #!/usr/bin/ruby -w ## grepauth - print lines that mention both foo and bar class MultiMatch def initialize(*patterns) _any = build_match('||',patterns) _all = build_match('&&',patterns) eval "def match_any(line);#{_any};end\n" eval "def match_all(line);#{_all};end\n" end def build_match(sym,args) args.map{|e|"line =~ /#{e}/"}.join(sym) end end mm = MultiMatch.new('foo','bar') ARGF.each do |line| print line if mm.match_all(line) end #----------------------------- #!/usr/bin/ruby # popgrep4 - grep for abbreviations of places that say "pop" # version4: pretty fast, but simple: compile all re's first: popstates = %w(CO ON MI WI MN) popstates = popstates.map{|re| %r/\b#{re}\b/} ARGF.each do |line| popstates.each do |state_re| if line =~ state_re print line break end end end ## speeds trials on the jargon file(412): 26006 lines, 1.3MB ## popgrep1 => 7.040s ## popgrep2 => 0.656s ## popgrep2.5 => 0.633s ## popgrep3 => 0.675s ## popgrep4 => 1.027s # unless speed is criticial, the technique in popgrep4 is a # reasonable balance between speed and logical simplicity. # @@PLEAC@@_6.11 begin print "Pattern? " pat = $stdin.gets.chomp Regexp.new(pat) rescue warn "Invalid Pattern" retry end # @@PLEAC@@_6.13 # uses the 'amatch' extension found on: # http://raa.ruby-lang.org/project/amatch/ require 'amatch' matcher = Amatch.new('balast') #$relative, $distance = 0, 1 File.open('/usr/share/dict/words').each_line do |line| print line if matcher.search(line) <= 1 end #__END__ #CODE ballast ballasts balustrade balustrades blast blasted blaster blasters blasting blasts # @@PLEAC@@_6.14 str.scan(/\G(\d)/).each do |token| puts "found #{token}" end #----------------------------- n = " 49 here" n.gsub!(/\G /,'0') puts n #----------------------------- str = "3,4,5,9,120" str.scan(/\G,?(\d+)/).each do |num| puts "Found number: #{num}" end #----------------------------- # Ruby doesn't have the String.pos or a /c re modifier like Perl # But it does have StringScanner in the standard library (strscn) # which allows similar functionality: require 'strscan' text = 'the year 1752 lost 10 days on the 3rd of September' sc = StringScanner.new(text) while sc.scan(/.*?(\d+)/) print "found: #{sc[1]}\n" end if sc.scan(/\S+/) puts "Found #{sc[0]} after last number" end #----------------------------- # assuming continuing from above: puts "The position in 'text' is: #{sc.pos}" sc.pos = 30 puts "The position in 'text' is: #{sc.pos}" # @@PLEAC@@_6.15 #----------------------------- # greedy pattern str.gsub!(/<.*>/m,'') # not good # non-greedy (minimal) pattern str.gsub!(/<.*?>/m,'') # not great #----------------------------- #this and that are important Oh, me too! #----------------------------- %r{ (.*?) }mx #----------------------------- %r/BEGIN((?:(?!BEGIN).)*)END/ #----------------------------- %r{ ( (?: (?!|). )* ) }mx #----------------------------- %r{ ( (?: (?!). )* ) }mx #----------------------------- %r{ [^<]* # stuff not possibly bad, and not possibly the end. (?: # at this point, we can have '<' if not part of something bad (?! ) # what we can't have < # okay, so match the '<' [^<]* # and continue with more safe stuff ) * }mx # @@PLEAC@@_6.16 #----------------------------- $/ = "" ARGF.each do |para| para.scan %r/ \b # start at word boundary (\S+) # find chunk of non-whitespace \b # until a word boundary ( \s+ # followed by whitespace \1 # and that same chunk again \b # and a word boundary ) + # one or more times /xi do puts "dup word '#{$1}' at paragraph #{$.}" end end #----------------------------- astr = 'nobody' bstr = 'bodysnatcher' if "#{astr} #{bstr}" =~ /^(\w+)(\w+) \2(\w+)$/ print "#{$2} overlaps in #{$1}-#{$2}-#{$3}" end #----------------------------- #!/usr/bin/ruby -w # prime_pattern -- find prime factors of argument using patterns ARGV << 180 cap = 'o' * ARGV.shift while cap =~ /^(oo+?)\1+$/ print $1.size, " " cap.gsub!(/#{$1}/,'o') end puts cap.size #----------------------------- #diophantine # solve for 12x + 15y + 16z = 281, maximizing x if ('o' * 281).match(/^(o*)\1{11}(o*)\2{14}(o*)\3{15}$/) x, y, z = $1.size, $2.size, $3.size puts "One solution is: x=#{x}; y=#{y}; z=#{z}" else puts "No solution." end # => One solution is: x=17; y=3; z=2 #----------------------------- # using different quantifiers: ('o' * 281).match(/^(o+)\1{11}(o+)\2{14}(o+)\3{15}$/) # => One solution is: x=17; y=3; z=2 ('o' * 281).match(/^(o*?)\1{11}(o*)\2{14}(o*)\3{15}$/) # => One solution is: x=0; y=7; z=11 ('o' * 281).match(/^(o+?)\1{11}(o*)\2{14}(o*)\3{15}$/) # => One solution is: x=1; y=3; z=14 # @@PLEAC@@_6.17 # alpha OR beta %r/alpha|beta/ # alpha AND beta %r/(?=.*alpha)(?=.*beta)/m # alpha AND beta, no overlap %r/alpha.*beta|beta.*alpha/m # NOT beta %r/^(?:(?!beta).)*$/m # NOT bad BUT good %r/(?=(?:(?!BAD).)*$)GOOD/m #----------------------------- if !(string =~ /pattern/) # ugly something() end if string !~ /pattern/ # preferred something() end #----------------------------- if string =~ /pat1/ && string =~ /pat2/ something() end #----------------------------- if string =~ /pat1/ || string =~ /pat2/ something() end #----------------------------- #!/usr/bin/ruby -w # minigrep - trivial grep pat = ARGV.shift ARGF.each do |line| print line if line =~ /#{pat}/o end #----------------------------- "labelled" =~ /^(?=.*bell)(?=.*lab)/m #----------------------------- $string =~ /bell/ && $string =~ /lab/ #----------------------------- $murray_hill = "blah bell blah " if $murray_hill =~ %r{ ^ # start of string (?= # zero-width lookahead .* # any amount of intervening stuff bell # the desired bell string ) # rewind, since we were only looking (?= # and do the same thing .* # any amount of intervening stuff lab # and the lab part ) }mx # /m means . can match newline print "Looks like Bell Labs might be in Murray Hill!\n"; end #----------------------------- "labelled" =~ /(?:^.*bell.*lab)|(?:^.*lab.*bell)/ #----------------------------- $brand = "labelled"; if $brand =~ %r{ (?: # non-capturing grouper ^ .*? # any amount of stuff at the front bell # look for a bell .*? # followed by any amount of anything lab # look for a lab ) # end grouper | # otherwise, try the other direction (?: # non-capturing grouper ^ .*? # any amount of stuff at the front lab # look for a lab .*? # followed by any amount of anything bell # followed by a bell ) # end grouper }mx # /m means . can match newline print "Our brand has bell and lab separate.\n"; end #----------------------------- $map =~ /^(?:(?!waldo).)*$/s #----------------------------- $map = "the great baldo" if $map =~ %r{ ^ # start of string (?: # non-capturing grouper (?! # look ahead negation waldo # is he ahead of us now? ) # is so, the negation failed . # any character (cuzza /s) ) * # repeat that grouping 0 or more $ # through the end of the string }mx # /m means . can match newline print "There's no waldo here!\n"; end =begin 7:15am up 206 days, 13:30, 4 users, load average: 1.04, 1.07, 1.04 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT tchrist tty1 5:16pm 36days 24:43 0.03s xinit tchrist tty2 5:19pm 6days 0.43s 0.43s -tcsh tchrist ttyp0 chthon 7:58am 3days 23.44s 0.44s -tcsh gnat ttyS4 coprolith 2:01pm 13:36m 0.30s 0.30s -tcsh =end #% w | minigrep '^(?!.*ttyp).*tchrist' #----------------------------- %r{ ^ # anchored to the start (?! # zero-width look-ahead assertion .* # any amount of anything (faster than .*?) ttyp # the string you don't want to find ) # end look-ahead negation; rewind to start .* # any amount of anything (faster than .*?) tchrist # now try to find Tom }x #----------------------------- #% w | grep tchrist | grep -v ttyp #----------------------------- #% grep -i 'pattern' files #% minigrep '(?i)pattern' files #----------------------------- # @@PLEAC@@_6.20 ans = $stdin.gets.chomp re = %r/^#{Regexp.quote(ans)}/ case when "SEND" =~ re : puts "Action is send" when "STOP" =~ re : puts "Action is stop" when "ABORT" =~ re : puts "Action is abort" when "EDIT" =~ re : puts "Action is edit" end #----------------------------- require 'abbrev' table = Abbrev.abbrev %w-send stop abort edit- loop do print "Action: " ans = $stdin.gets.chomp puts "Action for #{ans} is #{table[ans.downcase]}" end #----------------------------- # dummy values are defined for 'file', 'PAGER', and # the 'invoke_editor' and 'deliver_message' methods # do not do anything interesting in this example. #!/usr/bin/ruby -w require 'abbrev' file = 'pleac_ruby.data' PAGER = 'less' def invoke_editor puts "invoking editor" end def deliver_message puts "delivering message" end actions = { 'edit' => self.method(:invoke_editor), 'send' => self.method(:deliver_message), 'list' => proc {system(PAGER, file)}, 'abort' => proc {puts "See ya!"; exit}, "" => proc {puts "Unknown Command"} } dtable = Abbrev.abbrev(actions.keys) loop do print "Action: " ans = $stdin.gets.chomp.delete(" \t") actions[ dtable[ans.downcase] || "" ].call end # @@PLEAC@@_6.19 #----------------------------- # basically, the Perl Cookbook categorizes this as an # unsolvable problem ... #----------------------------- 1 while addr.gsub!(/\([^()]*\)/,'') #----------------------------- Dear someuser@host.com, Please confirm the mail address you gave us Wed May 6 09:38:41 MDT 1998 by replying to this message. Include the string "Rumpelstiltskin" in that reply, but spelled in reverse; that is, start with "Nik...". Once this is done, your confirmed address will be entered into our records. # @@PLEAC@@_6.21 #----------------------------- #% gunzip -c ~/mail/archive.gz | urlify > archive.urlified #----------------------------- #% urlify ~/mail/*.inbox > ~/allmail.urlified #----------------------------- #!/usr/bin/ruby -w # urlify - wrap HTML links around URL-like constructs urls = '(https?|telnet|gopher|file|wais|ftp)'; ltrs = '\w'; gunk = '/#~:.?+=&%@!\-'; punc = '.:?\-'; any = "#{ltrs}#{gunk}#{punc}"; ARGF.each do |line| line.gsub! %r/ \b # start at word boundary ( # begin $1 { #{urls} : # need resource and a colon [#{any}] +? # followed by on or more # of any valid character, but # be conservative and take only # what you need to.... ) # end $1 } (?= # look-ahead non-consumptive assertion [#{punc}]* # either 0 or more punctuation [^#{any}] # followed by a non-url char | # or else $ # then end of the string ) /iox do %Q|#{$1}| end print line end # @@PLEAC@@_6.23 %r/^m*(d?c{0,3}|c[dm])(l?x{0,3}|x[lc])(v?i{0,3}|i[vx])$/i #----------------------------- str.sub!(/(\S+)(\s+)(\S+)/, '\3\2\1') #----------------------------- %r/(\w+)\s*=\s*(.*)\s*$/ # keyword is $1, value is $2 #----------------------------- %r/.{80,}/ #----------------------------- %r|(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)| #----------------------------- str.gsub!(%r|/usr/bin|,'/usr/local/bin') #----------------------------- str.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/){ $1.hex.chr } #----------------------------- str.gsub!(%r{ /\* # Match the opening delimiter .*? # Match a minimal number of characters \*/ # Match the closing delimiter }xm,'') #----------------------------- str.sub!(/^\s+/, '') str.sub!(/\s+$/, '') # but really, in Ruby we'd just do: str.strip! #----------------------------- str.gsub!(/\\n/,"\n") #----------------------------- str.sub!(/^.*::/, '') #----------------------------- %r/^([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])\. ([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])$/x #----------------------------- str.sub!(%r|^.*/|, '') #----------------------------- cols = ( (ENV['TERMCAP'] || " ") =~ /:co#(\d+):/ ) ? $1 : 80; #----------------------------- name = " #{$0} #{ARGV}".gsub(%r| /\S+/|, ' ') #----------------------------- require 'rbconfig' include Config raise "This isn't Linux" unless CONFIG['target_os'] =~ /linux/i; #----------------------------- str.gsub!(%r/\n\s+/, ' ') #----------------------------- nums = str.scan(/(\d+\.?\d*|\.\d+)/) #----------------------------- capwords = str.scan(%r/(\b[^\Wa-z0-9_]+\b)/) #----------------------------- lowords = str.scan(%r/(\b[^\WA-Z0-9_]+\b)/) #----------------------------- icwords = str.scan(%r/(\b[^\Wa-z0-9_][^\WA-Z0-9_]*\b)/) #----------------------------- links = str.scan(%r/]+?HREF\s*=\s*["']?([^'" >]+?)[ '"]?>/mi) #----------------------------- initial = str =~ /^\S+\s+(\S)\S*\s+\S/ ? $1 : "" #----------------------------- str.gsub!(%r/"([^"]*)"/, %q-``\1''-) #----------------------------- $/ = "" sentences = [] ARGF.each do |para| para.gsub!(/\n/, ' ') para.gsub!(/ {3,}/,' ') sentences << para.scan(/(\S.*?[!?.])(?= |\Z)/) end #----------------------------- %r/(\d{4})-(\d\d)-(\d\d)/ # YYYY in $1, MM in $2, DD in $3 #----------------------------- %r/ ^ (?: 1 \s (?: \d\d\d \s)? # 1, or 1 and area code | # ... or ... \(\d\d\d\) \s # area code with parens | # ... or ... (?: \+\d\d?\d? \s)? # optional +country code \d\d\d ([\s\-]) # and area code ) \d\d\d (\s|\1) # prefix (and area code separator) \d\d\d\d # exchange $ /x #----------------------------- %r/\boh\s+my\s+gh?o(d(dess(es)?|s?)|odness|sh)\b/i #----------------------------- lines = [] lines << $1 while input.sub!(/^([^\012\015]*)(\012\015?|\015\012?)/,'') # @@PLEAC@@_7.0 # An IO object being Enumerable, we can use 'each' directly on it File.open("/usr/local/widgets/data").each { |line| puts line if line =~ /blue/ } logfile = File.new("/var/log/rubylog.txt", "w") mysub($stdin, logfile) # The method IO#readline is similar to IO#gets # but throws an exception when it reaches EOF f = File.new("bla.txt") begin while (line = f.readline) line.chomp $stdout.print line if line =~ /blue/ end rescue EOFError f.close end while $stdin.gets # reads from STDIN unless (/\d/) $stderr.puts "No digit found." # writes to STDERR end puts "Read: #{$_}" # writes to STDOUT end logfile = File.new("/tmp/log", "w") logfile.close # $defout (or its synonym '$>') is the destination of output # for Kernel#print, Kernel#puts, and family functions logfile = File.new("log.txt", "w") old = $defout $defout = logfile # switch to logfile for output puts "Countdown initiated ..." $defout = old # return to original output puts "You have 30 seconds to reach minimum safety distance." # @@PLEAC@@_7.1 source = File.new(path, "r") # open file "path" for reading only sink = File.new(path, "w") # open file "path" for writing only source = File.open(path, File::RDONLY) # open file "path" for reading only sink = File.open(path, File::WRONLY) # open file "path" for writing only file = File.open(path, "r+") # open "path" for reading and writing file = File.open(path, flags) # open "path" with the flags "flags" (see examples below for flags) # open file "path" read only file = File.open(path, "r") file = File.open(path, File::RDONLY) # open file "path" write only, create it if it does not exist # truncate it to zero length if it exists file = File.open(path, "w") file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT) file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT, 0666) # with permission 0666 # open file "path" write only, fails if file exists file = File.open(path, File::WRONLY|File::EXCL|File::CREAT) file = File.open(path, File::WRONLY|File::EXCL|File::CREAT, 0666) # open file "path" for appending file = File.open(path, "a") file = File.open(path, File::WRONLY|File::APPEND|File::CREAT) file = File.open(path, File::WRONLY|File::APPEND|File::CREAT, 0666) # open file "path" for appending only when file exists file = File.open(path, File::WRONLY|File::APPEND) # open file "path" for reading and writing file = File.open(path, "r+") file = File.open(path, File::RDWR) # open file for reading and writing, create a new file if it does not exist file = File.open(path, File::RDWR|File::CREAT) file = File.open(path, File::RDWR|File::CREAT, 0600) # open file "path" reading and writing, fails if file exists file = File.open(path, File::RDWR|File::EXCL|File::CREAT) file = File.open(path, File::RDWR|File::EXCL|File::CREAT, 0600) # @@PLEAC@@_7.2 # No problem with Ruby since the filename doesn't contain characters with # special meaning; like Perl's sysopen File.open(filename, 'r') # @@PLEAC@@_7.3 File.expand_path('~root/tmp') #=> "/root/tmp" File.expand_path('~rpcuser') #=> "/var/lib/nfs" # To expand ~/.. it explicitely needs the environment variable HOME File.expand_path('~/tmp') #=> "/home/gc/tmp" # @@PLEAC@@_7.4 # The exception raised in Ruby reports the filename File.open('afile') # @@PLEAC@@_7.5 # Standard Ruby distribution provides the following useful extension require 'tempfile' # With the Tempfile class, the file is automatically deleted on garbage # collection, so you won't need to remove it, later on. tf = Tempfile.new('tmp') # a name is required to create the filename # If you need to pass the filename to an external program you can use # File#path, but don't forget to File#flush in order to flush anything # living in some buffer somewhere. tf.flush system("/usr/bin/dowhatever #{tf.path}") fh = Tempfile.new('tmp') fh.sync = true # autoflushes 10.times { |i| fh.puts i } fh.rewind puts 'Tmp file has: ', fh.readlines # @@PLEAC@@_7.6 while (DATA.gets) do # process the line end #__END__ # your data goes here # __DATA__ doesn't exist in Ruby #CODE # get info about the script (size, date of last modification) kilosize = DATA.stat.size / 1024 last_modif = DATA.stat.mtime puts "

Script size is #{kilosize}" puts "

Last script update: #{last_modif}" #__END__ # DO NOT REMOVE THE PRECEEDING LINE. # Everything else in this file will be ignored. #CODE # @@PLEAC@@_7.7 while line = gets do # do something with line. end # or while gets do # do something with $_ end # or more rubyish $stdun.each do |line| # do stuff with line end # ARGF may makes this more easy # this is skipped if ARGV.size==0 ARGV.each do |filename| # closing and exception handling are done by the block open(filename) do |fd| fd.each do |line| # do stuff with line end end rescue abort("can't open %s" % filename) end # globbing is done in the Dir module ARGV = Dir["*.[Cch]"] if ARGV.empty? # note: optparse is the preferred way to handle this if (ARGV[0] == '-c') chop_first += 1 ARGV.shift end # processing numerical options if ARGV[0] =~ /^-(\d+)$/ columns = $1 ARGV.shift end # again, better to use optparse: require 'optparse' nostdout = 0 append = 0 unbuffer = 0 ignore_ints = 0 ARGV.options do |opt| opt.on('-n') { nostdout +=1 } opt.on('-a') { append +=1 } opt.on('-u') { unbuffer +=1 } opt.on('-i') { ignore_ints +=1 } opt.parse! end or abort("usage: " + __FILE__ + " [-ainu] [filenames]") # no need to do undef $/, we have File.read str = File.read(ARGV[0]) # again we have File.read str = File.read(ARGV[0]) # not sure what this should do: # I believe open the file, print filename, lineno and line: ARGF.each_with_index do |line, idx| print ARGF.filename, ":", idx, ";", line end # print all the lines in every file passed via command line that contains login ARGF.each do |line| puts line if line =~ /login/ end # # even this would fit #%ruby -ne "print if /f/" 2.log # ARGF.each { |l| puts l.downcase! } #------------------ #!/usr/bin/ruby -p # just like perl's -p $_.downcase! # # I don't know who should I trust. # perl's version splits on \w+ while python's on \w. chunks = 0 File.read(ARGV[0]).split.each do |word| next if word =~ /^#/ break if ["__DATA__", "__END__"].member? word chunks += 1 end print "Found ", chunks, " chunks\n" # @@PLEAC@@_7.8 old = File.open(old_file) new = File.open(new_file, "w") while old.gets do # change $_, then... new.print $_ end old.close new.close File.rename(old_file, "old.orig") File.rename(new_file, old_file) while old.gets do if $. == 20 then # we are at the 20th line new.puts "Extra line 1" new.puts "Extra line 2" end new.print $_ end while old.gets do next if 20..30 # skip the 20th line to the 30th # Ruby (and Perl) permit to write if 20..30 # instead of if (20 <= $.) and ($. <= 30) new.print $_ end # @@PLEAC@@_7.9 #% ruby -i.orig -pe 'FILTER COMMAND' file1 file2 file3 ... # #----------------------------- ##!/usr/bin/ruby -i.orig -p # filter commands go here #----------------------------- #% ruby -pi.orig -e 'gsub!(/DATE/){Time.now)' # effectively becomes: ARGV << 'I' oldfile = "" while gets if ARGF.filename != oldfile newfile = ARGF.filename File.rename(newfile, newfile + ".orig") $stdout = File.open(newfile,'w') oldfile = newfile end gsub!(/DATE/){Time.now} print end $stdout = STDOUT #----------------------------- #% ruby -i.old -pe 'gsub!(%r{\bhisvar\b}, 'hervar')' *.[Cchy] #----------------------------- # set up to iterate over the *.c files in the current directory, # editing in place and saving the old file with a .orig extension $-i = '.orig' # set up -i mode ARGV.replace(Dir['*.[Cchy]']) while gets if $. == 1 print "This line should appear at the top of each file\n" end gsub!(/\b(p)earl\b/i, '\1erl') # Correct typos, preserving case print ARGF.close if ARGF.eof end # @@PLEAC@@_7.10 File.open('itest', 'r+') do |f| # open file for update lines = f.readlines # read into array of lines lines.each do |it| # modify lines it.gsub!(/foo/, 'QQQ') end f.pos = 0 # back to start f.print lines # write out modified lines f.truncate(f.pos) # truncate to new length end # file is automatically closed #----------------------------- File.open('itest', 'r+') do |f| out = "" f.each do |line| out << line.gsub(/DATE/) {Time.now} end f.pos = 0 f.print out f.truncate(f.pos) end # @@PLEAC@@_7.11 File.open('infile', 'r+') do |f| f.flock File::LOCK_EX # update file end #----------------------------- File::LOCK_SH # shared lock (for reading) File::LOCK_EX # exclusive lock (for writing) File::LOCK_NB # non-blocking request File::LOCK_UN # free lock #----------------------------- unless f.flock File::LOCK_EX | File::LOCK_NB warn "can't get immediate lock: blocking ..." f.flock File::LOCK_EX end #----------------------------- File.open('numfile', File::RDWR|File::CREAT) do |f| f.flock(File::LOCK_EX) num = f.gets.to_i || 0 f.pos = 0 f.truncate 0 f.puts num + 1q end # @@PLEAC@@_7.12 output_handle.sync = true # Please note that like in Perl, $stderr is already unbuffered #----------------------------- #!/usr/bin/ruby -w # seeme - demo stdio output buffering $stdout.sync = ARGV.size > 0 print "Now you don't see it..." sleep 2 puts "now you do" #----------------------------- $stderr.sync = true afile.sync = false #----------------------------- # assume 'remote_con' is an interactive socket handle, # but 'disk_file' is a handle to a regular file. remote_con.sync = true # unbuffer for clarity disk_file.sync = false # buffered for speed #----------------------------- require 'socket' sock = TCPSocket.new('www.ruby-lang.org', 80) sock.sync = true sock.puts "GET /en/ HTTP/1.0 \n\n" resp = sock.read print "DOC IS: #{resp}\n" # @@PLEAC@@_7.13 #----------------------------- # assumes fh1, fh2, fh2 are oen IO objects nfound = select([$stdin, fh1, fh2, fh3], nil, nil, 0) nfound[0].each do |file| case file when fh1 # do something with fh1 when fh2 # do something with fh2 when fh3 # do something with fh3 end end #----------------------------- input_files = [] # repeat next line for all in-files to poll input_files << fh1 if nfound = select(input_files, nil, nil, 0) # input ready on files in nfound[0] end # @@PLEAC@@_8.0 #----------------------------- # datafile is a file or IO object datafile.readlines.each { |line| line.chomp! size = line.length puts size } #----------------------------- datafile.readlines.each { |line| puts line.chomp!.length } #----------------------------- lines = datafile.readlines #----------------------------- whole_file = file.read #----------------------------- # ruby -040 -e 'word = gets; puts "First word is #{word}"' #----------------------------- # ruby -ne 'BEGIN { $/="%%\n" }; $_.chomp; puts $_ if( $_=~/Unix/i)' fortune.dat #----------------------------- handle.print "one", "two", "three" # "onetwothree" puts "Baa baa black sheep." # sent to $stdout #----------------------------- buffer = handle.read(4096) rv = buffer.length #----------------------------- handle.truncate(length) open("/tmp#{$$}.pid", 'w') { |handle| handle.truncate(length) } #----------------------------- pos = datafile.pos # tell is an alias of pos puts "I'm #{pos} bytes from the start of datafile" #----------------------------- logfile.seek(0, IO::SEEK_END) datafile.seek(pos) # IO::SEEK_SET is the default out.seek(-20, IO::SEEK_CUR) #----------------------------- written = datafile.syswrite(mystring) raise RunTimeError unless written == mystring.length block = infile.sysread(256) # no equivalent to perl offset parameter in sysread puts "only read #{block.length} bytes" if 256 != block.length #----------------------------- pos = handle.sysseek(0, IO::SEEK_CUR) # don't change position # @@PLEAC@@_8.1 while (line = fh.gets) line.chomp! nextline = nil line.gsub!(/\\$/) { |match| nextline = fh.gets; '' } if (nextline != nil) line += nextline redo end # process full record in line here end #----------------------------- # DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \ # $(TEXINFOS) $(INFOS) $(MANS) $(DATA) # DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \ # $(TEXINFOS) $(INFO_DEPS) $(MANS) $(DATA) \ # $(EXTRA_DIST) #----------------------------- line.gsub!(/\\\s*$/, '') { # as before } # @@PLEAC@@_8.2 #----------------------------- count = `wc -l < #{filename}` fail "wc failed: #{$?}" if $? != 0 count.chomp! #----------------------------- count = 0 File.open(file, 'r') { |fh| count += 1 while fh.gets } # count now holds the number of lines read #----------------------------- count = 0 while (chunk = file.sysread(2**16)) count += chunk.count("\n") end rescue EOFError #----------------------------- File.open(filename,'r') { |fh| count += 1 while fh.gets } # count now holds the number of lines read #----------------------------- # As ruby doesn't quite have an equivalent to using a for # statement as in perl, I threw this in count = File.readlines(filename).size #----------------------------- 1 while file.gets count = $. #----------------------------- $/ = '' open(filename, 'r') { |fh| 1 while fh.gets para_count = $. } rescue fail("can't open #{filename}: $!") #----------------------------- # ^^PLEAC^^_8.3 #----------------------------- while (gets) split.each { |chunk| # do something with chunk } end #----------------------------- while (gets) gsub(/(\w[\w'-]*)/) { |word| # do something with word } end #----------------------------- # Make a word frequency count # normally hashes can be created using {} or just Hash.new # but we want the default value of an entry to be 0 instead # of nil. (nil can't be incremented) seen = Hash.new(0) while (gets) gsub(/(\w[\w'-]*)/) { |word| seen[word.downcase] += 1 } end # output hash in a descending numeric sort of its values seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v| printf("%5d %s\n", v, k ) end #----------------------------- # Line frequency count seen = Hash.new(0) while (gets) seen[$_.downcase] += 1 end seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v| printf("%5d %s\n", v, k ) end #----------------------------- # @@PLEAC@@_8.4 #----------------------------- # instead of file handle FILE, we can just # use a string containing the filename File.readlines(file).each { |line| # do something with line } #----------------------------- File.readlines(file).reverse_each { |line| # do something with line } #----------------------------- # the variable lines might have been created # this way # lines = File.readlines(file) # # normally one would use the reverse_each, but # if you insist on using a numerical index to # iterate over the lines array... (lines.size - 1).downto(0) { |i| line = lines[i] } #----------------------------- # the second readlines argument is a the # record separator $/, just like perl, a blank # separator splits the records into paragraphs File.readlines(file, '').each { |paragraph| # do something with paragraph puts "->Paragraph #{paragraph}" } #----------------------------- # @@PLEAC@@_8.6 $/ = "%\n"; srand; File.open('/usr/share/fortune/humorists').each do |line| adage = line if rand($.) < 1 end puts adage; # @@PLEAC@@_8.10 begin fh = File.open(file, "r+") addr = fh.tell unless fh.eof while fh.gets fh.truncate(addr) rescue SystemCallError $stderr.puts "#$!" end # @@PLEAC@@_9.0 entry = File.stat("/usr/bin/vi") entry = File.stat("/usr/bin") entry = File.stat(INFILE) entry = File.stat("/usr/bin/vi") ctime = entry.ctime size = entry.size f = File.open(filename, "r") ## There is no -T equivalent in Ruby, but we can still test emptiness if test(?s, filename) puts "#{filename} doesn't have text in it." exit end Dir.new("/usr/bin").each do |filename| puts "Inside /usr/bin is something called #{filename}" end # @@PLEAC@@_9.1 file = File.stat("filename") readtime, writetime = file.atime, file.mtime file.utime(readtime, writetime) SECONDS_PER_DAY = 60 * 60 * 24 file = File.stat("filename") atime, mtime = file.atime, file.mtime atime -= 7 * SECONDS_PER_DAY mtime -= 7 * SECONDS_PER_DAY File.utime(atime, mtime, file) mtime = File.stat(file).mtime File.utime(Time.new, mtime, file) File.utime(Time.new, File.stat("testfile").mtime, file) #----------------------------- #!/usr/bin/ruby -w ## uvi - vi a file without changing it's access times if ARGV.length != 1 puts "usage: uvi filename" exit end file = ARGV[0] atime, mtime = File.stat(file).atime, File.stat(file).mtime system(ENV["EDITOR"] || "vi", file) File.utime(atime, mtime, file) #----------------------------- # @@PLEAC@@_9.2 File.unlink(FILENAME) err_flg = false filenames.each do |file| begin File.unlink(file) rescue err_flg = $! end end err_flg and raise "Couldn't unlink all of #{filenames.join(" ")}: #{err_flg}" File.unlink(file) count = filenames.length filenames.each do |file| begin File.unlink(file) rescue count -= 1 end end if count != filenames.length STDERR.puts "could only delete #{count} of #{filenames.length} files" end # @@PLEAC@@_9.3 require "ftools" File.copy(oldfile, newfile) infile = File.open(oldfile, "r") outfile = File.open(newfile, "w") blksize = infile.stat.blksize # This doesn't handle partial writes or ^Z # like the Perl version does. while (line = infile.read(blksize)) outfile.write(line) end infile.close outfile.close system("cp #{oldfile} #{newfile}") # unix system("copy #{oldfile} #{newfile}") # dos, vms require "ftools" File.copy("datafile.dat", "datafile.bak") File.move("datafile.new", "datafile.dat") # @@PLEAC@@_9.4 $seen = {} # must use global var to be seen inside of method below def do_my_thing(filename) dev, ino = File.stat(filename).dev, File.stat(filename).ino unless $seen[[dev, ino]] # do something with $filename because we haven't # seen it before end $seen[[dev, ino]] = $seen[[dev, ino]].to_i + 1 end files.each do |filename| dev, ino = File.stat(filename).dev, File.stat(filename).ino if !$seen.has_key?([dev, ino]) $seen[[dev, ino]] = [] end $seen[[dev, ino]].push(filename) end $seen.keys.sort.each do |devino| ino, dev = devino if $seen[devino].length > 1 # $seen[devino] is a list of filenames for the same file end end # @@PLEAC@@_9.5 Dir.open(dirname) do |dir| dir.each do |file| # do something with dirname/file puts file end end # Dir.close is automatic # No -T equivalent in Ruby dir.each do |file| next if file =~ /^\.\.?$/ # ... end def plainfiles(dir) dh = Dir.open(dir) dh.entries.grep(/^[^.]/). map {|file| "#{dir}/#{file}"}. find_all {|file| test(?f, file)}. sort end # @@PLEAC@@_9.6 list = Dir.glob("*.c") dir = Dir.open(path) files = dir.entries.grep(/\.c$/) dir.close files = Dir.glob("*.c") files = Dir.open(path).entries.grep(/\.[ch]$/i) dir = Dir.new(path) files = dir.entries.grep(/\.[ch]$/i) begin d = Dir.open(dir) rescue Errno::ENOENT raise "Couldn't open #{dir} for reading: #{$!}" end files = [] d.each do |file| puts file next unless file =~ /\.[ch]$/i filename = "#{dir}/#{file}" # There is no -T equivalent in Ruby, but we can still test emptiness files.push(filename) if test(?s, filename) end dirs.entries.grep(/^\d+$/). map { |file| [file, "#{path}/#{file}"]} . select { |file| test(?d, file[1]) }. sort { |a,b| a[0] <=> b[0] }. map { |file| file[1] } # @@PLEAC@@_9.7 require 'find' Find.find(dirlist) do |file| # do whatever end require 'find' argv = ARGV.empty? ? %w{.} : ARGV Find.find(*argv) do |file| print file, (test(?d, file) ? "/\n" : "\n") end require 'find' argv = ARGV.empty? ? %w{.} : ARGV sum = 0 Find.find(*argv) do |file| size = test(?s, file) || 0 sum += size end puts "#{argv.join(' ')} contains #{sum} bytes" require 'find' argv = ARGV.empty? ? %w{.} : ARGV saved_size, saved_name = -1, "" Find.find(*argv) do |file| size = test(?s, file) || 0 next unless test(?f, file) && size > saved_size saved_size = size saved_name = file end puts "Biggest file #{saved_name} in #{argv.join(' ')} is #{saved_size}" require 'find' argv = ARGV.empty? ? %w{.} : ARGV age, name = nil Find.find(*argv) do |file| mtime = File.stat(file).mtime next if age && age > mtime age = mtime name = file end puts "#{name} #{age}" #----------------------------- #!/usr/bin/ruby -w # fdirs - find all directories require 'find' argv = ARGV.empty? ? %w{.} : ARGV File.find(*argv) { |file| puts file if test(?d, file) } #----------------------------- # @@PLEAC@@_9.8 require 'fileutils' puts "Usage #{$0} dir ..." if ARGV.empty? ARGV.each do |dir| FileUtils.rmtree(dir) end # @@PLEAC@@_9.9 require 'ftools' names.each do |file| newname = file begin File.move(file, newname) rescue Errno::EPERM $stderr.puts "Couldn't rename #{file} to #{newname}: #{$!}" end end require 'ftools' op = ARGV.empty? ? (raise "Usage: rename expr [files]\n") : ARGV.shift argv = ARGV.empty? ? $stdin.readlines.map { |f| f.chomp } : ARGV argv.each do |file| was = file file = eval("file.#{op}") File.move(was, file) unless was == file end # @@PLEAC@@_9.10 base = File.basename(path) dir = File.dirname(path) # ruby has no fileparse equivalent dir, base = File.split(path) ext = base.scan(/\..*$/).to_s path = '/usr/lib/libc.a' file = File.basename(path) dir = File.dirname(path) puts "dir is #{dir}, file is #{file}" # dir is /usr/lib, file is libc.a path = '/usr/lib/libc.a' dir, filename = File.split(path) name, ext = filename.split(/(?=\.)/) puts "dir is #{dir}, name is #{name}, ext is #{ext}" # NOTE: The Ruby code prints # dir is /usr/lib, name is libc, extension is .a # while the Perl code prints a '/' after the directory name # dir is /usr/lib/, name is libc, extension is .a # No fileparse_set_fstype() equivalent in ruby def extension(path) ext = path.scan(/\..*$/).to_s ext.sub(/^\./, "") end # @@PLEAC@@_9.11 #----------------------------- #!/usr/bin/ruby -w # symirror - build spectral forest of symlinks require 'find' require 'fileutils' raise "usage: #{$0} realdir mirrordir" unless ARGV.size == 2 srcdir,dstdir = ARGV srcmode = File::stat(srcdir).mode Dir.mkdir(dstdir, srcmode & 07777) unless test(?d, dstdir) # fix relative paths Dir.chdir(srcdir) {srcdir = Dir.pwd} Dir.chdir(dstdir) {dstdir = Dir.pwd} Find.find(srcdir) do |srcfile| if test(?d, srcfile) dest = srcfile.sub(/^#{srcdir}/, dstdir) dmode = File::stat(srcfile).mode & 07777 Dir.mkdir(dest, dmode) unless test(?d, dest) a = Dir["#{srcfile}/*"].reject{|f| test(?d, f)} FileUtils.ln_s(a, dest) end end # @@PLEAC@@_9.12 # we use the Getopt/Declare library here for convenience: # http://raa.ruby-lang.org/project/getoptdeclare/ #----------------------------- #!/usr/bin/ruby -w # lst - list sorted directory contents (depth first) require 'find' require 'etc' require "Getopt/Declare" # Note: in the option-spec below there must by at least one hard # tab in between each -option and its description. For example # -i read from stdin opts = Getopt::Declare.new(<<'EOPARAM') ============ Input Format: -i read from stdin ============ Output Format: -l long listing -r reverse listing ============ Sort on: (one of) -m mtime (modify time - default) {$sort_criteria = :mtime} -u atime (access time) {$sort_criteria = :atime} -c ctime (inode change time) {$sort_criteria = :ctime} -s size {$sort_criteria = :size} [mutex: -m -u -c -s] EOPARAM $sort_criteria ||= :mtime files = {} DIRS = opts['-i'] ? $stdin.readlines.map{|f|f.chomp!} : ARGV DIRS.each do |dir| Find.find(dir) do |ent| files[ent] = File::stat(ent) end end entries = files.keys.sort_by{|f| files[f].send($sort_criteria)} entries = entries.reverse unless opts['-r'] entries.each do |ent| unless opts['-l'] puts ent next end stats = files[ent] ftime = stats.send($sort_criteria == :size ? :mtime : $sort_criteria) printf "%6d %04o %6d %8s %8s %8d %s %s\n", stats.ino, stats.mode & 07777, stats.nlink, ETC::PASSWD[stats.uid].name, ETC::GROUP[stats.gid].name, stats.size, ftime.strftime("%a %b %d %H:%M:%S %Y"), ent end # @@PLEAC@@_10.0 def hello $greeted += 1 # in Ruby, a variable beginning with $ is global (can be any type of course) puts "hi there!" end # We need to initialize $greeted before it can be used, because "+=" is waiting a Numeric object $greeted = 0 hello # note that appending () is optional to function calls with no parameters # @@PLEAC@@_10.1 # In Ruby, parameters are named anyway def hypotenuse(side1, side2) Math.sqrt(side1**2 + side2**2) # the sqrt function comes from the Math module end diag = hypotenuse(3, 4) puts hypotenuse(3, 4) a = [3, 4] print hypotenuse(*a) # the star operator will magically convert an Array into a "tuple" both = men + women # In Ruby, all objects are references, so the same problem arises; we then return a new object nums = [1.4, 3.5, 6.7] def int_all(n) n.collect { |v| v.to_i } end ints = int_all(nums) nums = [1.4, 3.5, 6.7] def trunc_em(n) n.collect! { |v| v.to_i } # the bang-version of collect modifies the object end trunc_em(nums) # Ruby has two chomp version: # ``chomp'' chomps the record separator and returns what's expected # ``chomp!'' does the same but also modifies the parameter object # @@PLEAC@@_10.2 def somefunc variable = something # variable is local by default end name, age = ARGV start = fetch_time a, b = pair # will succeed if pair is an Array object (like ARGV is) c = fetch_time # In ruby, run_check can't access a, b, or c until they are # explicitely defined global (using leading $), even if they are # both defined in the same scope def check_x(x) y = "whatever" run_check if $condition puts "got $x" end end # The following will keep a reference to the array, though the # results will be slightly different from perl: the last element # of $global_array will be itself an array def save_array(ary) $global_array << ary end # The following gives the same results as in Perl for $global_array, # though it doesn't illustrate anymore the way to keep a reference # to an object: $global_array is extended with the elements of ary def save_array(ary) $global_array += ary end # @@PLEAC@@_10.3 # In Ruby, AFAIK a method cannot access "local variables" defined # upper scope; mostly because everything is an object, so you'll # do the same by defining an attribute or a static attribute # In Ruby the BEGIN also exists: BEGIN { puts "hello from BEGIN" } puts "hello from main" BEGIN { puts "hello from 2nd BEGIN" } # gives: # hello from BEGIN # hello from 2nd BEGIN # hello from main # In Ruby, it can be written as a static method and a static # variable class Counter @@counter = 0 def Counter.next_counter; @@counter += 1; end end # There is no need of BEGIN since the variable will get # initialized when parsing class Counter @@counter = 42 def Counter.next_counter; @@counter += 1; end def Counter.prev_counter; @@counter -= 1; end end # @@PLEAC@@_10.4 # You can either get the whole trace as an array of strings, each # string telling which file, line and method is calling: caller # ...or only the last caller caller[0] # We need to extract just the method name of the backtrace: def whoami; caller()[0] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end def whowasi; caller()[1] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end # @@PLEAC@@_10.5 # In Ruby, every value is a reference on an object, thus there is # no such problem array_diff(array1, array2) def add_vecpair(a1, a2) results = [] a1.each_index { |i| results << (a1[i] + a2[i]) } results end a = [1, 2] b = [5, 8] c = add_vecpair(a, b) p c # Add this to the beginning of the function to check if we were # given two arrays a1.type == Array && a2.type == Array or raise "usage: add_vecpair array1 array2 (was used with: #{a1.type} #{a2.type})" # @@PLEAC@@_10.6 # There is no return context in Ruby # @@PLEAC@@_10.7 # Like in Perl, we need to fake with a hash, but it's dirty :-( def thefunc(param_args) args = { 'INCREMENT' => '10s', 'FINISH' => '0', 'START' => 0 } args.update(param_args) if (args['INCREMENT'] =~ /m$/ ) # ..... end end thefunc({ 'INCREMENT' => '20s', 'START' => '+5m', 'FINISH' => '+30m' }) thefunc({}) # @@PLEAC@@_10.8 # there is no "undef" direct equivalent but there is the slice equiv: a, c = func.indexes(0, 2) # @@PLEAC@@_10.9 # Ruby has no such limitation: def somefunc ary = [] hash = {} # ... return ary, hash end arr, dict = somefunc array_of_hashes = fn h1, h2, h3 = fn # @@PLEAC@@_10.10 return # or (equivalent) return nil # @@PLEAC@@_10.11 # You can't prototype in Ruby regarding types :-( # Though, you can force the number of arguments: def func_with_no_arg; end def func_with_no_arg(); end def func_with_one_arg(a1); end def func_with_two_args(a1, a2); end def func_with_any_number_of_args(*args); end # @@PLEAC@@_10.12 raise "some message" # raise exception begin val = func rescue Exception => msg $stderr.puts "func raised an exception: #{msg}" end # In Ruby the rescue statement uses an exception class, every # exception which is not matched is still continuing begin val = func rescue FullMoonError ... end # @@PLEAC@@_10.13 # Saving Global Values # Of course we can just save the value and restore it later: def print_age puts "Age is #{$age}" end $age = 18 # global variable print_age() if condition safeage = $age $age = 23 print_age() $age = safeage end # We can also use a method that saves the global variable and # restores it automatically when the block is left: def local(var) eval("save = #{var.id2name}") begin result = yield ensure # we want to call this even if we got an exception eval("#{var.id2name} = save") end result end condition = true $age = 18 print_age() if condition local(:$age) { $age = 23 print_age() } end print_age() # There is no need to use local() for filehandles or directory # handles in ruby because filehandles are normal objects. # @@PLEAC@@_10.14 # In Ruby you may redefine a method [but not overload it :-(] # just by defining again with the same name. def foo; puts 'foo'; end def foo; puts 'bar'; end foo #=> bar # You can also take a reference to an existing method before # redefining a new one, using the `alias' keyword def foo; puts 'foo'; end alias foo_orig foo def foo; puts 'bar'; end foo_orig foo #=> foo #=> bar # AFAIK, there is no direct way to create a new method whose name # comes from a variable, so use "eval" colors = %w(red blue green yellow orange purple violet) colors.each { |c| eval <<-EOS def #{c}(*a) "" + a.to_s + "" end EOS } # @@PLEAC@@_10.15 def method_missing(name, *args) "" + args.join(' ') + "" end puts chartreuse("stuff") # @@PLEAC@@_10.16 def outer(arg) x = arg + 35 inner = proc { x * 19 } x + inner.call() end # @@PLEAC@@_10.17 #!/usr/bin/ruby -w # mailsort - sort mbox by different criteria require 'English' require 'Date' # Objects of class Mail represent a single mail. class Mail attr_accessor :no attr_accessor :subject attr_accessor :fulltext attr_accessor :date def initialize @fulltext = "" @subject = "" end def append(para) @fulltext << para end # this is called if you call puts(mail) def to_s @fulltext end end # represents a list of mails. class Mailbox < Array Subjectpattern = Regexp.new('Subject:\s*(?:Re:\s*)*(.*)\n') Datepattern = Regexp.new('Date:\s*(.*)\n') # reads mails from open file and stores them def read(file) $INPUT_RECORD_SEPARATOR = '' # paragraph reads msgno = -1 file.each { |para| if para =~ /^From/ mail = Mail.new mail.no = (msgno += 1) md = Subjectpattern.match(para) if md mail.subject = md[1] end md = Datepattern.match(para) if md mail.date = DateTime.parse(md[1]) else mail.date = DateTime.now end self.push(mail) end mail.append(para) if mail } end def sort_by_subject_and_no self.sort_by { |m| [m.subject, m.no] } end # sorts by a list of attributs of mail, given as symbols def sort_by_attributs(*attrs) # you can sort an Enumerable by an array of # values, they would be compared # from ary[0] to ary[n]t, say: # ['b',1] > ['a',10] > ['a',9] self.sort_by { |elem| attrs.map { |attr| elem.send(attr) } } end end mailbox = Mailbox.new mailbox.read(ARGF) # print only subjects sorted by subject and number for m in mailbox.sort_by_subject_and_no puts(m.subject) end # print complete mails sorted by date, then subject, then number for m in mailbox.sort_by_attributs(:date, :subject) puts(m) end # @@PLEAC@@_11.7 def mkcounter(count) start = count bundle = { "NEXT" => proc { count += 1 }, "PREV" => proc { count -= 1 }, "RESET" => proc { count = start } } bundle["LAST"] = bundle["PREV"] return bundle end c1 = mkcounter(20) c2 = mkcounter(77) puts "next c1: #{c1["NEXT"].call}" # 21 puts "next c2: #{c2["NEXT"].call}" # 78 puts "next c1: #{c1["NEXT"].call}" # 22 puts "last c1: #{c1["PREV"].call}" # 21 puts "last c1: #{c1["LAST"].call}" # 20 puts "old c2: #{c2["RESET"].call}" # 77 # @@PLEAC@@_11.15 class Binary_tree def initialize(val) @value = val @left = nil @right = nil end # insert given value into proper point of # provided tree. If no tree provided, # use implicit pass by reference aspect of @_ # to fill one in for our caller. def insert(val) if val < @value then if @left then @left.insert(val) else @left = Binary_tree.new(val) end elsif val > @value then if @right then @right.insert(val) else @right = Binary_tree.new(val) end else puts "double" # do nothing, no double values end end # recurse on left child, # then show current value, # then recurse on right child. def in_order @left.in_order if @left print @value, " " @right.in_order if @right end # show current value, # then recurse on left child, # then recurse on right child. def pre_order print @value, " " @left.pre_order if @left @right.pre_order if @right end # recurse on left child, # then recurse on right child, # then show current value. def post_order @left.post_order if @left @right.post_order if @right print @value, " " end # find out whether provided value is in the tree. # if so, return the node at which the value was found. # cut down search time by only looking in the correct # branch, based on current value. def search(val) if val == @value then return self elsif val < @value then return @left.search(val) if @left return nil else return @right.search(val) if @right return nil end end end # first generate 20 random inserts test = Binary_tree.new(0) for a in 0..20 test.insert(rand(1000)) end # now dump out the tree all three ways print "Pre order: "; test.pre_order; puts "" print "In order: "; test.in_order; puts "" print "Post order: "; test.post_order; puts "" print "search?" while gets print test.search($_.to_i) print "\nsearch?" end # @@PLEAC@@_12.0 # class and module names need to have the first letter capitalized module Alpha NAME = 'first' end module Omega NAME = 'last' end puts "Alpha is #{Alpha::NAME}, Omega is #{Omega::NAME}" # ruby doesn't differentiate beteen compile-time and run-time require 'getoptlong.rb' require 'getoptlong' # assumes the .rb require 'cards/poker.rb' require 'cards/poker' # assumes the .rb load 'cards/poker' # require only loads the file once module Cards module Poker @card_deck = Array.new # or @card_deck = [] def shuffle end end end # @@PLEAC@@_12.1 # a module exports all of its functions module Your_Module def self.function # this would be called as Your_Module.function end def Your_Module.another # this is the same as above, but more specific end end # @@PLEAC@@_12.2 begin require 'nonexistent' rescue LoadError puts "Couldn't load #{$!}" # $! contains the last error string end # @@PLEAC@@_12.4 # module variables are private unless access functions are defined module Alpha @aa = 10 @bb = 11 def self.put_aa puts @aa end def self.bb=(val) @bb = val end end Alpha.bb = 12 # Alpha.aa = 10 # error, no aa=method # @@PLEAC@@_12.5 # caller provides a backtrace of the call stack module MyModule def find_caller caller end def find_caller2(i) caller(i) # an argument limits the size of the stack returned end end # @@PLEAC@@_12.6 BEGIN { $logfile = '/tmp/mylog' unless defined? $logfile $LF = File.open($logfile, 'a') } module Logger def self.logmsg(msg) $LF.puts msg end logmsg('startup') end END { Logger::logmsg('shutdown') $LF.close } # @@PLEAC@@_12.7 #----------------------------- # results may be different on your system # % ruby -e "$LOAD_PATH.each_index { |i| printf("%d %s\n", i, $LOAD_PATH[i] } #0 /usr/local/lib/site_ruby/1.6 #1 /usr/local/lib/site_ruby/1.6/i386-linux #2 /usr/local/lib/site_ruby/ #3 /usr/lib/ruby/1.6 #4 /usr/lib/ruby/1.6/i136-linux #5 . #----------------------------- # syntax for sh, bash, ksh, or zsh #$ export RUBYLIB=$HOME/rubylib # syntax for csh or tcsh # % setenv RUBYLIB ~/rubylib #----------------------------- $LOAD_PATH.unshift "/projects/spectre/lib"; # @@PLEAC@@_12.8 # equivalents in ruby are mkmf, SWIG, or Ruby/DL depending on usage # @@PLEAC@@_12.9 # no equivalent in ruby # @@PLEAC@@_12.10 # no equivalent in ruby # @@PLEAC@@_12.11 module FineTime def self.time # to be defined later end end module FineTime def self.time "its a fine time" end end puts FineTime.time #=> "its a fine time" # @@PLEAC@@_12.12 def even_only(n) raise "#{n} is not even" if (n & 1) != 0 # one way to test # ... end def even_only(n) $stderr.puts "#{n} is not even" if (n & 1) != 0 # ... end # @@PLEAC@@_12.17 # The library archive for ruby is called Ruby Application archive, # or shorter RAA, and can be found at http://raa.ruby-lang.org. # A typical library is installed like this: # % gunzip some-module-4.54.tar.gz # % tar xf some-module-4.54.tar # % cd some-module-4.54.tar # % ruby install.rb config # % ruby install.rb setup # get superuser previleges here if needed for next step # % ruby install.rb install # Some modules use a different process, # you should find details in the documentation # Here is an example of such a different process # % ruby extconf.rb # % make # % make install # If you want the module installed in your own directory: # For ruby version specific libraries # % ruby install.rb config --site-ruby=~/lib # For version independent libraries # % ruby install.rb config --site-ruby-common=~/lib # Information about possible options for config # % ruby install.rb --help # If you have your own complete distribution # % ruby install.rb --prefix=path=~/ruby-private # @@PLEAC@@_13.0 # Classes and objects in Ruby are rather straigthforward class Person # Class variables (also called static attributes) are prefixed by @@ @@person_counter=0 # object constructor def initialize(age, name, alive = true) # Default arg like in C++ @age, @name, @alive = age, name, alive # Object attributes are prefixed by '@' @@person_counter += 1 # There is no '++' operator in Ruby. The '++'/'--' operators are in fact # hidden assignments which affect variables, not objects. You cannot accomplish # assignment via method. Since everything in Ruby is object, '++' and '--' # contradict Ruby OO ideology. Instead '-=' and '+=' are used. end attr_accessor :name, :age # This creates setter and getter methods for @name # and @age. See 13.3 for detailes. # methods modifying the receiver object usually have the '!' suffix def die! @alive = false puts "#{@name} has died at the age of #{@age}." @alive end def kill(anotherPerson) print @name, ' is killing ', anotherPerson.name, ".\n" anotherPerson.die! end # methods used as queries # usually have the '?' suffix def alive? @alive && true end def year_of_birth Time.now.year - @age end # Class method (also called static method) def Person.number_of_people @@person_counter end end # Using the class: # Create objects of class Person lecter = Person.new(47, 'Hannibal') starling = Person.new(29, 'Clarice', true) pazzi = Person.new(40, 'Rinaldo', true) # Calling a class method print "There are ", Person.number_of_people, " Person objects\n" print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n" lecter.kill(pazzi) print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n" print starling.name , ' was born in ', starling.year_of_birth, "\n" # @@PLEAC@@_13.1 # If you don't need any initialisation in the constructor, # you don't need to write a constructor. class MyClass end class MyClass def initialize @start = Time.new @age = 0 end end class MyClass def initialize(inithash) @start = Time.new @age = 0 for key, value in inithash instance_variable_set("@#{key}", value) end end end # @@PLEAC@@_13.2 # Objects are destroyed by the garbage collector. # The time of destroying is not predictable. # The ruby garbage collector can handle circular references, # so there is no need to write destructor for that. # There is no direct support for destructor. # You can call a custom function, or more specific a proc object, when the # garbage collector is about to destruct the object, but it is unpredictable # when this occurs. # Also if such a finalizer object has a reference to the orignal object, # this may prevent the original object to get garbage collected. # Because of this problem the finalize method below is # a class method and not a instance method. # So if you need to free resources for an object, like # closing a socket or kill a spawned subprocess, # you should do it explicitly. class MyClass def initialize ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc) end def MyClass.finalize(id) puts "Object #{id} dying at #{Time.new}" end end # test code 3.times { MyClass.new } ObjectSpace.garbage_collect # @@PLEAC@@_13.3 # You can write getter and setter methods in a natural way: class Person def name @name end def name=(name) @name = name end end # But there is a better and shorter way class Person attr_reader :age attr_writer :name # attr_reader and attr_writer are actually methods in class Class # which set getter and setter methods for you. end # There is also attr_accessor to create both setters and getters class Person attr_accessor :age, :name end # @@PLEAC@@_13.4 class Person # Class variables (also called static attributes) are prefixed by @@ @@person_counter = 0 def Person.population @@person_counter end def initialize @@person_counter += 1 ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc) end def Person.finalize(id) @@person_counter -= 1 end end people = [] 10.times { people.push(Person.new) } printf("There are %d people alive", Person.population) FixedArray.class_max_bounds = 100 alpha = FixedArray.new puts "Bound on alpha is #{alpha.max_bounds}" beta = FixedArray.new beta.max_bounds = 50 # calls the instance method beta.class.class_max_bounds = 50 # alternative, calls the class method puts "Bound on alpha is #{alpha.max_bounds}" class FixedArray @@bounds = 7 def max_bounds @@max_bounds end # instance method, which sets the class variable def max_bounds=(value) @@max_bounds = value end # class method. This can only be called on a class, # but not on the instances def FixedArray.class_max_bounds=(value) @@max_bounds = value end end # @@PLEAC@@_13.5 PersonStruct = Struct.new("Person", :name, :age, :peers) # creates a class "Person::Struct", which is accessiable with the # constant "PersonStruct" p = PersonStruct.new p = Struct::Person.new # alternative using the classname p.name = "Jason Smythe" p.age = 13 p.peers = ["Wilbur", "Ralph", "Fred"] p[:peers] = ["Wilbur", "Ralph", "Fred"] # alternative access using symbol p["peers"] = ["Wilbur", "Ralph", "Fred"] # alternative access using name of field p[2] = ["Wilbur", "Ralph", "Fred"] # alternative access using index of field puts "At age #{p.age}, #{p.name}'s first friend is #{p.peers[0]}" # The fields of a struct have no special type, like other ruby variables # you can put any objects in. Therefore the discussions how to specify # the types of the fields do not apply to ruby. FamilyStruct = Struct.new("Family", :head, :address, :members) folks = FamilyStruct.new folks.head = PersonStruct.new dad = folks.head dad.name = "John" dad.age = 34 # supply of own accessor method for the struct for error checking class PersonStruct def age=(value) if !value.kind_of?(Integer) raise(ArgumentError, "Age #{value} isn't an Integer") elsif value > 150 raise(ArgumentError, "Age #{value} is unreasonable") end @age = value end end # @@PLEAC@@_13.6 # The ruby Object class defines a dup and a clone method. # The dup method is recommended for prototype object creation. # The default implementation makes a shallow copy, # but each class can override it, for example to make a deep copy. # If you want to call 'new' directly on the instances, # you can create a instance method "new", which returns a new duplicate. # This method is distinct from the class method new. # class A def new dup end end ob1 = A.new # later on ob2 = ob1.new # @@PLEAC@@_13.7 methname = 'flicker' obj.send(methname, 10) # calls obj.flicker(10) # call three methods on the object, by name ['start', 'run', 'stop'].each do |method_string| obj.send(method_string) end # Another way is to create a Method object method_obj = obj.method('flicker') # And then call it method_obj.call(10) # @@PLEAC@@_13.8 # All classes in Ruby inherit from class Object # and thus all objects share methods defined in this class # the class of the object puts any_object.type # Ruby classes are actually objects of class Class and they # respond to methods defined in Object class as well # the superclass of this class puts any_object.class.superclass # ask an object whether it is an instance of particular class n = 4.7 puts n.instance_of?(Float) # true puts n.instance_of?(Numeric) # false # ask an object whether it is an instance of class, one of the # superclasses of the object, or modules included in it puts n.kind_of?(Float) # true (the class) puts n.kind_of?(Numeric) # true (an ancestor class) puts n.kind_of?(Comparable) # true (a mixin module) puts n.kind_of?(String) # false # ask an object whether it can respond to a particular method puts n.respond_to?('+') # true puts n.respond_to?('length') # false # all methods an object can respond to 'just a string'.methods.each { |m| puts m } # @@PLEAC@@_13.9 # Actually any class in Ruby is inheritable class Person attr_accessor :age, :name def initialize @name @age end end #----------------------------- dude = Person.new dude.name = 'Jason' dude.age = 23 printf "%s is age %d.\n", dude.name, dude.age #----------------------------- # Inheriting from Person class Employee < Person attr_accessor :salary end #----------------------------- empl = Employee.new empl.name = 'Jason' empl.age = 23 empl.salary = 200 printf "%s is age %d, the salary is %d.\n", empl.name, empl.age, empl.salary #----------------------------- # Any built-in class can be inherited the same way class WeirdString < String def initialize(obj) super obj end def +(anotherObj) # + method in this class is overridden # to return the sum of string lengths self.length + anotherObj.length # 'self' can be omitted end end #----------------------------- a = WeirdString.new('hello') b = WeirdString.new('bye') puts a + b # the overridden + #=> 8 puts a.length # method from the superclass, String #=> 5 # @@PLEAC@@_13.11 # In ruby you can override the method_missing method # to have a solution similar to perls AUTOLOAD. class Person def initialize @ok_fields = %w(name age peers parent) end def valid_attribute?(name) @ok_fields.include?(name) end def method_missing(namesymbol, *params) name = namesymbol.to_s return if name =~ /^A-Z/ if name.to_s[-1] == ('='[0]) # we have a setter isSetter = true name.sub!(/=$/, '') end if valid_attribute?(name) if isSetter instance_variable_set("@#{name}", *params) else instance_variable_get("@#{name}", *params) end else # if no annestor is responsible, # the Object class will throw a NoMethodError exception super(namesymbol, *params) end end def new kid = Person.new kid.parent = self kid end end dad = Person.new dad.name = "Jason" dad.age = 23 kid = dad.new kid.name = "Rachel" kid.age = 2 puts "Kid's parent is #{kid.parent.name}" puts dad puts kid class Employee < Person def initialize super @ok_fields.push("salary", "boss") end def ok_fields @ok_fields end end # @@PLEAC@@_13.13 # The ruby garbage collector pretends to cope with circular structures. # You can test it with this code: class RingNode attr_accessor :next attr_accessor :prev attr_reader :name def initialize(aName) @name = aName ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc) end def RingNode.finalize(id) puts "Node #{id} dying" end def RingNode.show_all_objects ObjectSpace.each_object {|id| puts id.name if id.class == RingNode } end end def create_test a = RingNode.new("Node A") b = RingNode.new("Node B") c = RingNode.new("Node C") a.next = b b.next = c c.next = a a.prev = c c.prev = b b.prev = a a = nil b = nil c = nil end create_test RingNode.show_all_objects ObjectSpace.garbage_collect puts "After garbage collection" RingNode.show_all_objects # @@PLEAC@@_13.14 class String def <=>(other) self.casecmp other end end # There is no way to directly overload the '""' (stringify) # operator in Ruby. However, by convention, classes which # can reasonably be converted to a String will define a # 'to_s' method as in the TimeNumber class defined below. # The 'puts' method will automatcally call an object's # 'to_s' method as is demonstrated below. # Furthermore, if a class defines a to_str method, an object of that # class can be used most any place where the interpreter is looking # for a String value. #--------------------------------------- # NOTE: Ruby has a builtin Time class which would usually be used # to manipulate time objects, the following is supplied for # educational purposes to demonstrate operator overloading. # class TimeNumber attr_accessor :hours,:minutes,:seconds def initialize( hours, minutes, seconds) @hours = hours @minutes = minutes @seconds = seconds end def to_s return sprintf( "%d:%02d:%02d", @hours, @minutes, @seconds) end def to_str to_s end def +( other) seconds = @seconds + other.seconds minutes = @minutes + other.minutes hours = @hours + other.hours if seconds >= 60 seconds %= 60 minutes += 1 end if minutes >= 60 minutes %= 60 hours += 1 end return TimeNumber.new(hours, minutes, seconds) end def -(other) raise NotImplementedError end def *(other) raise NotImplementedError end def /( other) raise NotImplementedError end end t1 = TimeNumber.new(0, 58, 59) sec = TimeNumber.new(0, 0, 1) min = TimeNumber.new(0, 1, 0) puts t1 + sec + min + min #----------------------------- # StrNum class example: Ruby's builtin String class already has the # capabilities outlined in StrNum Perl example, however the '*' operator # on Ruby's String class acts differently: It creates a string which # is the original string repeated N times. # # Using Ruby's String class as is in this example: x = "Red"; y = "Black" z = x+y r = z*3 # r is "RedBlackRedBlackRedBlack" puts "values are #{x}, #{y}, #{z}, and #{r}" print "#{x} is ", x < y ? "LT" : "GE", " #{y}\n" # prints: # values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack # Red is GE Black #----------------------------- class FixNum REGEX = /(\.\d*)/ DEFAULT_PLACES = 0 attr_accessor :value, :places def initialize(value, places = nil) @value = value if places @places = places else m = REGEX.match(value.to_s) if m @places = m[0].length - 1 else @places = DEFAULT_PLACES end end end def +(other) FixNum.new(@value + other.value, max(@places, other.places)) end def *(other) FixNum.new(@value * other.value, max(@places, other.places)) end def /(other) puts "Divide: #{@value.to_f/other.value.to_f}" result = FixNum.new(@value.to_f/other.value.to_f) result.places = max(result.places,other.places) result end def to_s sprintf("STR%s: %.*f", self.class.to_s , @places, @value) #. end def to_str to_s end def to_i #convert to int @value.to_i end def to_f #convert to float` @value.to_f end private def max(a,b) a > b ? a : b end end def demo() x = FixNum.new(40) y = FixNum.new(12, 0) puts "sum of #{x} and #{y} is #{x+y}" puts "product of #{x} and #{y} is #{x*y}" z = x/y puts "#{z} has #{z.places} places" unless z.places z.places = 2 end puts "div of #{x} by #{y} is #{z}" puts "square of that is #{z*z}" end if __FILE__ == $0 demo() end # @@PLEAC@@_14.1 # There are dbm, sdbm, gdbm modules # and the bdb module for accessing the berkeley db # sdbm seem to be available on the most systems, # so we use it here # require "sdbm" SDBM.open("filename", 0666) { |dbobj| # raises exception if open error # the returned sdbm-dbobj has most of the methods of a hash v = dbobj["key"] dbobj["key"] = "newvalue" if dbobj.has_key?("key") # ... end dbobj.delete("key2") } # database is open only inside the block. # It is also possible to use a open .. close pair: dbobj = SDBM.open("filename", 0666) #.. do something with dbobj dbobj.close #!/usr/bin/ruby -w # userstats - generate statistics on who is logged in # call with usernames as argument to display the totals # for the given usernames, call with "ALL" to display all users require "sdbm" filename = '/tmp/userstats.db' SDBM.open(filename, 0666) { |dbobj| if ARGV.length > 0 if ARGV[0] == "ALL" # ARGV is constant, so we need the variable userlist userlist = dbobj.keys().sort() else userlist = ARGV end userlist.each { |user| print "#{user}\t#{dbobj[user]}\n" } else who = `who` who.split("\n").each { |line| md = /^(\S+)/.match(line) raise "Bad line from who: #{line}" unless md # sdbm stores only strings, so "+=" doesn't work, # we need to convert them expicitly back to integer. if dbobj.has_key?(md[0]) dbobj[md[0]] = dbobj[md[0]].to_i + 1 else dbobj[md[0]] = "1" end } end } # @@PLEAC@@_14.2 # using open and clear dbobj = SDBM.open("filename", 0666) dbobj.clear() dbobj.close() # deleting file and recreating it # the filenames depend on the flavor of dbm you use, # for example sdbm has two files named filename.pag and filename.dir, # so you need to delete both files begin File.delete("filename") # raises Exception if not exist dbobj = SDBM.open("filename", 0666) rescue # add error handling here end # @@PLEAC@@_14.3 # sdbm2gdbm: converts sdbm database to a gdbm database require "sdbm" require "gdbm" unless ARGV.length == 2 fail "usage: sdbm2gdbm infile outfile" end infile = ARGV[0] outfile = ARGV[1] sdb = SDBM.open(infile) gdb = GDBM.open(outfile, 0666) sdb.each { |key, val| gdb[key] = val } gdb.close sdb.close # @@PLEAC@@_14.4 #!/usr/bin/ruby -w # dbmmerge: merges two dbm databases require "sdbm" unless ARGV.length == 3 fail "usage: dbmmerge indb1 indb2 outdb" end infile1 = ARGV[0] infile2 = ARGV[0] outfile = ARGV[2] in1 = SDBM.open(infile1, nil) in2 = SDBM.open(infile2, nil) outdb = SDBM.open(outfile, 0666) [in1, in2].each { |indb| indb.each { |key, val| if outdb.has_key?(key) # decide which value to set. # set outdb[key] if necessary else outdb[key] = val end } } in1.close in2.close outdb.close # @@PLEAC@@_14.7 # we write a tie method that extends the Array class. # It reads the file into the memory, executes the code block # in which you can manipulate the array as needed, and writes # the array back to the file after the end of the block execution class Array def tie(filename, flags) File.open(filename, flags) { |f| f.each_line { |line| self.push(line.chomp) } yield f.rewind each { |line| if line f.puts(line) else f.puts "" end } } end end array = Array.new array.tie("/tmp/textfile.txt", File::RDWR|File::CREAT) { array[4] = "a new line 4" } # The tied array can be manipulated like a normal array, # so there is no need for a special API, and the recno_demo program # to demonstrate is API is useless # tied array demo: show how to use array with a tied file filename = "db_file.txt" lines = Array.new File.unlink(filename) if File.exists?(filename) lines.tie(filename, File::RDWR | File::CREAT) { # first create a textfile to play with lines[0] = "zero" lines[1] = "one" lines[2] = "two" lines[3] = "three" lines[4] = "four" # print the records in order. # Opposed to perl, the tied array behaves exactly as a normal array puts "\nOriginal" for i in 0..(lines.length-1) puts "#{i}: #{lines[i]}" end #use push and pop a = lines.pop lines.push("last") puts("The last line was [#{a}]") #use shift and unshift a = lines.shift lines.unshift("first") puts("The first line was [#{a}]") # add record after record 2 i = 2 lines.insert(i + 1, "Newbie") # add record before record one i = 1 lines.insert(i, "New One") # delete record 3 lines.delete_at(3) #now print the records in reverse order puts "\nReverse" (lines.length - 1).downto(0){ |i| puts "#{i}: #{lines[i]}" } } # @@PLEAC@@_14.8 # example to store complex data in a database # uses marshall from the standard library require "sdbm" db = SDBM.open("pleac14-8-database", 0666) # convert the Objects into strings and back by using the Marshal module. # Most normal objects can be converted out of the box, # but not special things like procedure objects, # IO instance variables, singleton objects db["Tom Christiansen"] = Marshal.dump(["book author", "tchrist@perl.com"]) db["Tom Boutell"] = Marshal.dump(["shareware author", "boutell@boutell.com"]) name1 = "Tom Christiansen" name2 = "Tom Boutell" tom1 = Marshal.load(db[name1]) tom2 = Marshal.load(db[name2]) puts "Two Toming: #{tom1} #{tom2}" if tom1[0] == tom2[0] && tom1[1] == tom2[1] puts "You're having runtime fun with one Tom made two." else puts "No two Toms are ever alike" end # To change parts of an entry, get the whole entry, change the parts, # and save the whole entry back entry = Marshal.load(db["Tom Boutell"]) entry[0] = "Poet Programmer" db["Tom Boutell"] = Marshal.dump(entry) db.close # @@PLEAC@@_14.9 # example to make data persistent # uses Marshal from the standard lib # Stores the data in a simple file, # see 14.8 on how to store it in a dbm file # The BEGIN block is executed before the rest of the script # we use global variables here because local variables # will go out of scope and are not accessible from the main script BEGIN { $persistent_store = "persitence.dat" begin File.open($persistent_store) do |f| $stringvariable1 = Marshal.load(f) $arrayvariable2 = Marshal.load(f) end rescue puts "Can not open #{$persistent_store}" # Initialisation if this script runs the first time $stringvariable1 = "" $arrayvariable2 = [] end } END { File.open($persistent_store, "w+") do |f| Marshal.dump($stringvariable1, f) Marshal.dump($arrayvariable2, f) end } # simple test program puts $stringvariable1 puts $arrayvariable2 $stringvariable1 = "Hello World" $arrayvariable2.push(5) puts $stringvariable1 puts $arrayvariable2 # @@PLEAC@@_14.10 #!/usr/bin/ruby -w # Ruby has a dbi module with an architecture similar # to the Perl dbi module: the dbi module provides an unified # interface and uses specialized drivers for each dbms vendor # begin DBI.connect("DBI:driver:driverspecific", "username", "auth") { |dbh| dbh.do(SQL1) dbh.prepare(SQL2){ |sth| sth.execute sth.fetch {|row| # ... } } # end of block finishes the statement handle } # end of block closes the database connection rescue DBI::DatabaseError => e puts "dbi error occurred" puts "Error code: #{e.err}" puts "Error message: #{e.errstr}" end #!/usr/bin/ruby -w # dbusers - example for mysql which creates a table, # fills it with values, retrieves the values back, # and finally destroys the table. require "dbi" # replacement for the User::pwnt module def getpwent result = [] File.open("/etc/passwd") {|file| file.each_line {|line| next if line.match(/^#/) cols = line.split(":") result.push([cols[2], cols[0]]) } } result end begin DBI.connect("DBI:Mysql:pleacdatabase", "pleac", "pleacpassword") { |conn| conn.do("CREATE TABLE users (uid INT, login CHAR(8))") users = getpwent conn.prepare("INSERT INTO users VALUES (?,?)") {|sth| users.each {|entry| sth.execute(entry[0], entry[1]) } } conn.execute("SELECT uid, login FROM users WHERE uid < 50") {|sth| sth.fetch {|row| puts row.collect {|col| if col.nil? "(null)" else col end }.join(", ") } } conn.do("DROP TABLE users") } rescue DBI::DatabaseError => e puts "dbi error occurred" puts "Error code: #{e.err}" puts "Error message: #{e.errstr}" end # @@PLEAC@@_15.1 # This test program demonstrates parsing program arguments. # It uses the optparse library, which is included with ruby 1.8 # It handles classic unix style and gnu style options require 'optparse' @debugmode = false @verbose = false ARGV.options do |opts| opts.banner = "Usage: ruby #{$0} [OPTIONS] INPUTFILES" opts.on("-h", "--help", "show this message") { puts opts exit } # The OptionParser#on method is called with a specification of short # options, of long options, a data type spezification and user help # messages for this option. # The method analyses the given parameter and decides what it is, # so you can leave out the long option if you don't need it opts.on("-v", "--[no-]verbose=[FLAG]", TrueClass, "run verbosly") { |@verbose| # sets @verbose to true or false } opts.on("-D", "--DEBUG", TrueClass, "turns on debug mode" ){ |@debugmode| # sets @debugmode to true } opts.on("-c", "--count=NUMBER", Integer, "how many times we do it" ){ |@count| # sets @count to given integer } opts.on("-o", "--output=FILE", String, "file to write output to"){ |@outputfile| # sets @outputfile to given string } opts.parse! end # example to use the options in the main program puts "Verbose is on" if @verbose puts "Debugmode is on" if @debugmode puts "Outfile is #{@outputfile}" if defined? @outputfile puts "Count is #{@count}" if defined? @count ARGV.each { |param| puts "Got parameter #{param}" } # @@PLEAC@@_15.4 buf = "\0" * 8 $stdout.ioctl(0x5413, buf) ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("S4") raise "You must have at least 20 characters" unless ws_col >= 20 max = 0 values = (1..5).collect { rand(20) } # generate an array[5] of rand values for i in values max = i if max < i end ratio = Float(ws_col-12)/max # chars per unit for i in values printf "%8.1f %s\n", i, "*" * (ratio*i) end # gives, for example: # 15.0 ******************************* # 10.0 ********************* # 5.0 ********** # 14.0 ***************************** # 18.0 ************************************** # @@PLEAC@@_16.1 output = `program args` # collect output into one multiline string output = `program args`.split # collect output into array, one line per element readme = IO.popen("ls") output = "" while readme.gets do output += $_ end readme.close `fsck -y /dev/rsd1a` # BAD AND SCARY in Perl because it's managed by the shell # I donna in Ruby ... # so the "clean and secure" version readme, writeme = IO.pipe pid = fork { # child $stdout = writeme readme.close exec('find', '..') } # parent Process.waitpid(pid, 0) writeme.close while readme.gets do # do something with $_ end # @@PLEAC@@_16.2 status = system("xemacs #{myfile}") status = system("xemacs", myfile) system("cmd1 args | cmd2 | cmd3 >outfile") system("cmd args outfile 2>errfile") # stop if the command fails raise "$program exited funny: #{$?}" unless system("cmd", "args1", "args2") # get the value of the signal sent to the child # even if it is a SIGINT or SIGQUIT system(arglist) raise "program killed by signal #{$?}" if ($? & 127) != 0 pid = fork { trap("SIGINT", "IGNORE") exec("sleep", "10") } trap ("SIGINT") { puts "Tsk tsk, no process interruptus" } Process.waitpid(pid, 0) # Ruby doesn't permit to lie to the program called by a 'system'. # (ie specify what return argv[0] in C, $0 in Perl/Ruby ...) # A (dirty) way is to create a link (under Unix), run this link and # erase it. Somebody has a best idea ? # @@PLEAC@@_16.3 exec("archive *.data") exec("archive", "accounting.data") exec("archive accounting.data") # @@PLEAC@@_16.4 # read the output of a program IO.popen("ls") {|readme| while readme.gets do # ... end } # or readme = IO.popen("ls") while readme.gets do # ... end readme.close # "write" in a program IO.popen("cmd args","w") {|pipe| pipe.puts("data") pipe.puts("foo") } # close wait for the end of the process read = IO.popen("sleep 10000") # child goes to sleep read.close # and the parent goes to lala land writeme = IO.popen("cmd args", "w") writeme.puts "hello" # program will get hello\n on STDIN writeme.close # program will get EOF on STDIN # send in a pager (eg less) all output $stdout = IO.popen("/usr/bin/less","w") print "huge string\n" * 10000 # @@PLEAC@@_16.5 #----------------------------- def head(lines = 20) pid = open("|-","w") if pid == nil return else while gets() do pid.print lines -= 1 break if lines == 0 end end exit end head(100) while gets() do print end #----------------------------- 1: > Welcome to Linux, version 2.0.33 on a i686 2: > 3: > "The software required `Windows 95 or better', 4: > so I installed Linux." #----------------------------- > 1: Welcome to Linux, Kernel version 2.0.33 on a i686 > 2: > 3: "The software required `Windows 95 or better', > 4: so I installed Linux." #----------------------------- #!/usr/bin/ruby # qnumcat - demo additive output filters def number() pid = open("|-","w") if pid == nil return else while gets() do pid.printf("%d: %s", $., $_); end end exit end def quote() pid = open("|-","w") if pid == nil return else while gets() do pid.print "> #{$_}" end end exit end number() quote() while gets() do print end $stdout.close exit # @@PLEAC@@_16.6 ARGV.map! { |arg| arg =~ /\.(gz|Z)$/ ? "|gzip -dc #{arg}" : arg } for file in ARGV fh = open(file) while fh.gets() do # ....... end end #----------------------------- ARGV.map! { |arg| arg =~ %r#^\w+://# ? "|GET #{arg}" : arg # } for file in ARGV fh = open(file) while fh.gets() do # ....... end end #----------------------------- pwdinfo = (`domainname` =~ /^(\(none\))?$/) ? '/etc/passwd' : '|ypcat passwd'; pwd = open(pwdinfo); #----------------------------- puts "File, please? "; file = gets().chomp(); fh = open(file); # @@PLEAC@@_16.7 output = `cmd 2>&1` # with backticks # or ph = open("|cmd 2>&1") # with an open pipe while ph.gets() { } # plus a read #----------------------------- output = `cmd 2>/dev/null` # with backticks # or ph = open("|cmd 2>/dev/null") # with an open pipe while ph.gets() { } # plus a read #----------------------------- output = `cmd 2>&1 1>/dev/null` # with backticks # or ph = open("|cmd 2>&1 1>/dev/null") # with an open pipe while ph.gets() { } # plus a read #----------------------------- output = `cmd 3>&1 1>&2 2>&3 3>&-` # with backticks # or ph = open("|cmd 3>&1 1>&2 2>&3 3>&-") # with an open pipe while ph.gets() { } # plus a read #----------------------------- system("program args 1>/tmp/program.stdout 2>/tmp/program.stderr") #----------------------------- output = `cmd 3>&1 1>&2 2>&3 3>&-` #----------------------------- fd3 = fd1 fd1 = fd2 fd2 = fd3 fd3 = undef #----------------------------- system("prog args 1>tmpfile 2>&1") system("prog args 2>&1 1>tmpfile") #----------------------------- # system ("prog args 1>tmpfile 2>&1") fd1 = "tmpfile" # change stdout destination first fd2 = fd1 # now point stderr there, too #----------------------------- # system("prog args 2>&1 1>tmpfile") fd2 = fd1 # stderr same destination as stdout fd1 = "tmpfile" # but change stdout destination #----------------------------- # It is often better not to rely on the shell, # because of portability, possible security problems # and bigger resource usage. So, it is often better to use the open3 library. # See below for an example. # opening stdin, stdout, stderr require "open3" stdin, stdout, stderr = Open3.popen('cmd') # @@PLEAC@@_16.8 #----------------------------- # Contrary to perl, we don't need to use a module in Ruby fh = Kernel.open("|" + program, "w+") fh.puts "here's your input\n" output = fh.gets() fh.close() #----------------------------- Kernel.open("|program"),"w+") # RIGHT ! #----------------------------- # Ruby has already object methods for I/O handles #----------------------------- begin fh = Kernel.open("|" + program_and_options, "w+") rescue if ($@ ~= /^open/) $stderr.puts "open failed : #{$!} \n #{$@} \n" break end raise # reraise unforseen exception end # @@PLEAC@@_16.13 #% kill -l #HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE #ALRM TERM CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM #PROF WINCH POLL PWR #----------------------------- #% ruby -e 'puts Signal.list.keys.join(" ")' #PWR USR1 BUS USR2 TERM SEGV KILL POLL STOP SYS TRAP IOT HUP INT # #WINCH XCPU TTIN CLD TSTP FPE IO TTOU PROF CHLD CONT PIPE ABRT #VTALRM QUIT ILL XFSZ URG ALRM #----------------------------- # After that, the perl script create an hash equivalent to Signal.list, # and an array. The array can be obtained by : signame = [] Signal.list.each { |name, i| signame[i] = name } # @@PLEAC@@_16.14 Process.kill(9, pid) # send $pid a signal 9 Process.kill(-1, Process.getpgrp()) # send whole job a signal 1 Process.kill("USR1", $$) # send myself a SIGUSR1 Process.kill("HUP", pid1, pid2, pid3) # send a SIGHUP to processes in @pids #----------------------------- begin Process.kill(0, minion) puts "#{minion} is alive!" rescue Errno::EPERM # changed uid puts "#{minion} has escaped my control!"; rescue Errno::ESRCH puts "#{minion} is deceased."; # or zombied rescue puts "Odd; I couldn't check the status of #{minion} : #{$!}" end # @@PLEAC@@_16.15 Kernel.trap("QUIT", got_sig_quit) # got_sig_quit = Proc.new { puts "Quit\n" } trap("PIPE", "got_sig_quit") # def got_sig_pipe ... trap("INT") { ouch++ } # increment ouch for every SIGINT #----------------------------- trap("INT", "IGNORE") # ignore the signal INT #----------------------------- trap("STOP", "DEFAULT") # restore default STOP signal handling # @@PLEAC@@_16.16 # the signal handler def ding trap("INT", "ding") puts "\aEnter your name!" end # prompt for name, overriding SIGINT def get_name save = trap("INT", "ding") puts "Kindly Stranger, please enter your name: " name = gets().chomp() trap("INT", save) name end # @@PLEAC@@_16.21 # implemented thanks to http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/1760 require 'timeout' # we'll do something vastly more useful than cookbook to demonstrate timeouts begin timeout(5) { waitsec = rand(10) puts "Let's see if a sleep of #{waitsec} seconds is longer than 5 seconds..." system("sleep #{waitsec}") } puts "Timeout didn't occur" rescue Timeout::Error puts "Timed out!" end # @@PLEAC@@_17.1 # A basic TCP client connection require 'socket' begin t = TCPSocket.new('www.ruby-lang.org', 'www') rescue puts "error: #{$!}" else # ... do something with the socket t.print "GET / HTTP/1.0\n\n" answer = t.gets(nil) # and terminate the connection when we're done t.close end # Using the evil low level socket API require 'socket' # create a socket s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) # build the address of the remote machine sockaddr_server = [Socket::AF_INET, 80, Socket.gethostbyname('www.ruby-lang.org')[3], 0, 0].pack("snA4NN") # connect begin s.connect(sockaddr_server) rescue puts "error: #{$!}" else # ... do something with the socket s.print "GET / HTTP/1.0\n\n" # and terminate the connection when we're done s.close end # TCP connection with management of error (DNS) require 'socket' begin client = TCPSocket.new('does not exists', 'www') rescue puts "error: #{$!}" end # TCP connection with a time out require 'socket' require 'timeout' begin timeout(1) do #the server has one second to answer client = TCPSocket.new('www.host.com', 'www') end rescue puts "error: #{$!}" end # @@PLEAC@@_17.12 require 'socket' class Preforker attr_reader (:child_count) def initialize(prefork, max_clients_per_child, port, client_handler) @prefork = prefork @max_clients_per_child = max_clients_per_child @port = port @child_count = 0 @reaper = proc { trap('CHLD', @reaper) pid = Process.wait @child_count -= 1 } @huntsman = proc { trap('CHLD', 'IGNORE') trap('INT', 'IGNORE') Process.kill('INT', 0) exit } @client_handler=client_handler end def child_handler trap('INT', 'EXIT') @client_handler.setUp # wish: sigprocmask UNblock SIGINT @max_clients_per_child.times { client = @server.accept or break @client_handler.handle_request(client) client.close } @client_handler.tearDown end def make_new_child # wish: sigprocmask block SIGINT @child_count += 1 pid = fork do child_handler end # wish: sigprocmask UNblock SIGINT end def run @server = TCPserver.open(@port) trap('CHLD', @reaper) trap('INT', @huntsman) loop { (@prefork - @child_count).times { |i| make_new_child } sleep .1 } end end #----------------------------- #!/usr/bin/ruby require 'Preforker' class ClientHandler def setUp end def tearDown end def handle_request(client) # do stuff end end server = Preforker.new(1, 100, 3102, ClientHandler.new) server.run # @@PLEAC@@_18.2 require 'net/ftp' begin ftp = Net::FTP::new("ftp.host.com") ftp.login(username,password) ftp.chdir(directory) ftp.get(filename) ftp.put(filename) rescue Net::FTPError $stderr.print "FTP failed: " + $! ensure ftp.close() if ftp end # A better solution for a local use could be : Net::FTP::new("ftp.host.com") do |ftp| ftp.login(username,password) ftp.chdir(directory) ftp.get(filename) ftp.put(filename) end # If you have only one file to get, there is a simple solution : require 'open-uri' open("ftp://www.ruby-lang.org/path/filename") do |fh| # read from filehandle fh end #-------------------------------------------- # to wait a defined time for the connection, # use the timeout module require 'timeout' begin timeout(30){ ftp = Net::FTP::new("ftp.host.com") ftp.debug_mode = true } rescue Net::FTPError $stderr.puts "Couldn't connect." rescue Timeout::Error $stderr.puts "Timeout while connecting to server." end begin ftp.login() rescue Net::FTPError $stderr.print "Couldn't authentificate.\n" end begin ftp.login(username) rescue Net::FTPError $stderr.print "Still couldn't authenticate.\n" end begin ftp.login(username, password) rescue Net::FTPError $stderr.print "Couldn't authenticate, even with explicit username and password.\n" end begin ftp.login(username, password, account) rescue Net::FTPError $stderr.print "No dice. It hates me.\n" end #----------------------------- ftp.put(localfile, remotefile) #----------------------------- # Sending data from STDIN is not directly supported # by the ftp library module. A possible way to do it is to use the # storlines method directly to send raw commands to the ftp server. #----------------------------- ftp.get(remotefile, localfile) #----------------------------- ftp.get(remotefile) { |data| puts data } #----------------------------- ftp.chdir("/pub/ruby") print "I'm in the directory ", ftp.pwd(), "\n" #----------------------------- ftp.mkdir("/pub/ruby/new_dir") #----------------------------- lines = ftp.ls("/pub/ruby/") # => ["drwxr-xr-x 2 matz users 4096 July 17 1998 1.0", ... ] latest = ftp.dir("/pub/ruby/*.tgz").sort.last ftp.nlst("/pub/ruby") # => ["/pub/ruby/1.0", ... ] #----------------------------- ftp.quit() # @@PLEAC@@_18.6 require 'net/telnet' t = Net::Telnet::new( "Timeout" => 10, "Prompt" => /%/, "Host" => host ) t.login(username, password) files = t.cmd("ls") t.print("top") process_string = t.waitfor(/\d+ processes/) t.close #----------------------------- /[$%#>] \z/n #----------------------------- # In case of an error, the telnet module throws an exception. # For control of the behavior in case of an error, # you just need to catch the exceptions and do your custom # error handling. #----------------------------- begin telnet.login(username, password) rescue TimeoutError fail "Login failed !\n" end #----------------------------- telnet.waitfor('/--more--/') #----------------------------- telnet.waitfor(String => 'greasy smoke', Timeout => 30) # @@PLEAC@@_18.7 require 'ping' puts "#{host} is alive.\n" if Ping.pingecho(host); #----------------------------- # the ping module only use TCP ping, not ICMP even if we are root if Ping.pingecho("kingkong.com") puts "The giant ape lives!\n"; else puts "All hail mighty Gamera, friend of children!\n"; end # @@PLEAC@@_19.1 #!/usr/local/bin/ruby -w # hiweb - load CGI class to decode information given by web server require 'cgi' cgi = CGI.new('html3') # get a parameter from a form value = cgi.params['PARAM_NAME'][0] # output a document cgi.out { cgi.html { cgi.head { cgi.title { "Howdy there!" } } + cgi.body { cgi.p { "You typed: " + cgi.tt { CGI.escapeHTML(value) } } } } } require 'cgi' cgi = CGI.new who = cgi.param["Name"][0] # first param in list phone = cgi.param["Number"][0] picks = cgi.param["Choices"] # complete list print cgi.header( 'type' => 'text/plain', 'expires' => Time.now + (3 * 24 * 60 * 60) ) # @@PLEAC@@_19.3 #!/usr/local/bin/ruby -w # webwhoami - show web user's id require 'etc' print "Content-Type: text/plain\n\n" print "Running as " + Etc.getpwuid.name + "\n" # % ruby -wc cgi-script # just check syntax # % ruby -w cgi-script # params from stdin # (offline mode: enter name=value pairs on standard input) # name=joe # number=10 # ^D # % ruby -w cgi-script name=joe number=10 # run with mock form input # % ruby -d cgi-script name=joe number=10 # ditto, under the debugger # POST method script in csh # % (setenv HTTP_METHOD POST; ruby -w cgi-script name=joe number=10) # POST method script in sh # % HTTP_METHOD=POST perl -w cgi-script name=joe number=10 # @@PLEAC@@_19.4 # ruby has several security levels, the level "1" is similar to perls taint mode. # It can be switched on by providing the -T command line parameter # or by setting $SAFE to 1. Setting $SAFE to 2,3 or 4 restricts possible # harmful operations further. #!/usr/bin/ruby -T $SAFE = 1 File.open(ARGV[0], "w") # ruby warns with: # taint1.rb:2:in `initialize': Insecure operation - initialize (SecurityError) $SAFE = 1 file = ARGV[0] unless /^([\w.-]+)$/.match(file) raise "filename #{file} has invalid characters" end file = $1 # In ruby, even the back reference from a regular expression stays tainted. # you need to explicitly untaint the variable: file.untaint File.open(file, "w") # Race condition exists like in perl: unless File.exists(filename) # Wrong because of race condition File.open(filename, "w") end # @@PLEAC@@_19.10 preference_value = cgi.cookies["preference name"][0] packed_cookie = CGI::Cookie.new("name" => "preference name", "value" => "whatever you'd like", "expires" => Time.local(Time.now.year + 2, Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) ) cgi.header("cookie" => [packed_cookie]) #!/usr/local/bin/ruby -w # ic_cookies - sample CGI script that uses a cookie require 'cgi' cgi = CGI.new('html3') cookname = "favorite ice cream" favorite = cgi.params["flavor"][0] tasty = cgi.cookies[cookname][0] || 'mint' unless favorite cgi.out { cgi.html { cgi.head { cgi.title { "Ice Cookies" } } + cgi.body { cgi.h1 { "Hello Ice Cream" } + cgi.hr + cgi.form { cgi.p { "Please select a flavor: " + cgi.text_field("flavor", tasty ) } } + cgi.hr } } } else cookie = CGI::Cookie.new( "name" => cookname, "value" => favorite, "expires" => Time.local(Time.now.year + 2, Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) ) cgi.out("cookie" => [cookie]) { cgi.html { cgi.head { cgi.title { "Ice Cookies" } } + cgi.body { cgi.h1 { "Hello Ice Cream" } + cgi.p { "You chose as your favorite flavor `#{favorite}'." } } } } end # @@PLEAC@@_20.9 def templatefile(filename, fillings) aFile = File.new(filename, "r") text = aFile.read() aFile.close() pattern = Regexp.new('%%(.*?)%%') text.gsub!(pattern) { fillings[$1] || "" } text end fields = { 'username' => whats_his_name, 'count' => login_count, 'total' => minutes_used } puts templatefile('simple.template', fields) # @@INCOMPLETE@@ # An example using databases is missing coderay-1.1.3/bin/000077500000000000000000000000001366440446100137225ustar00rootroot00000000000000coderay-1.1.3/bin/coderay000077500000000000000000000131461366440446100153030ustar00rootroot00000000000000#!/usr/bin/env ruby require 'coderay' $options, args = ARGV.partition { |arg| arg[/^-[hv]$|--\w+/] } subcommand = args.first if /^\w/ === args.first subcommand = nil if subcommand && File.exist?(subcommand) args.delete subcommand def option? *options !($options & options).empty? end def tty? $stdout.tty? || option?('--tty') end def version puts <<-USAGE CodeRay #{CodeRay::VERSION} USAGE end def help puts <<-HELP This is CodeRay #{CodeRay::VERSION}, a syntax highlighting tool for selected languages. usage: coderay [-language] [input] [-format] [output] defaults: language detect from input file name or shebang; fall back to plain text input STDIN format detect from output file name or use terminal; fall back to HTML output STDOUT common: coderay file.rb # highlight file to terminal coderay file.rb -page > file.html # highlight file to HTML page coderay file.rb -div > file.html # highlight file to HTML snippet configure output: coderay file.py output.json # output tokens as JSON coderay file.py -loc # count lines of code in Python file configure input: coderay -python file # specify the input language coderay -ruby # take input from STDIN more: coderay stylesheet [style] # print CSS stylesheet HELP end def commands puts <<-COMMANDS general: highlight code highlighting (default command, optional) stylesheet print the CSS stylesheet with the given name (aliases: style, css) about: list [of] list all available plugins (or just the scanners|encoders|styles|filetypes) commands print this list help show some help version print CodeRay version COMMANDS end def print_list_of plugin_host plugins = plugin_host.all_plugins.map do |plugin| info = " #{plugin.plugin_id}: #{plugin.title}" aliases = (plugin.aliases - [:default]).map { |key| "-#{key}" }.sort_by { |key| key.size } if plugin.respond_to?(:file_extension) || !aliases.empty? additional_info = [] additional_info << aliases.join(', ') unless aliases.empty? info << " (#{additional_info.join('; ')})" end info << ' <-- default' if plugin.aliases.include? :default info end puts plugins.sort end if option? '-v', '--version' version end if option? '-h', '--help' help end case subcommand when 'highlight', nil if ARGV.empty? version help else signature = args.map { |arg| arg[/^-/] ? '-' : 'f' }.join names = args.map { |arg| arg.sub(/^-/, '') } case signature when /^$/ exit when /^ff?$/ input_file, output_file, = *names when /^f-f?$/ input_file, output_format, output_file, = *names when /^-ff?$/ input_lang, input_file, output_file, = *names when /^-f-f?$/ input_lang, input_file, output_format, output_file, = *names when /^--?f?$/ input_lang, output_format, output_file, = *names else $stdout = $stderr help puts puts "Unknown parameter order: #{args.join ' '}, expected: [-language] [input] [-format] [output]" exit 1 end if input_file input_lang ||= CodeRay::FileType.fetch input_file, :text, true end if output_file output_format ||= CodeRay::FileType[output_file] || :plain else output_format ||= :terminal end output_format = :page if output_format.to_s == 'html' if input_file input = File.read input_file else input = $stdin.read end begin file = if output_file File.open output_file, 'w' else $stdout end CodeRay.encode(input, input_lang, output_format, :out => file) file.puts rescue CodeRay::PluginHost::PluginNotFound => boom $stdout = $stderr if boom.message[/CodeRay::(\w+)s could not load plugin :?(.*?): /] puts "I don't know the #$1 \"#$2\"." else puts boom.message end # puts "I don't know this plugin: #{boom.message[/Could not load plugin (.*?): /, 1]}." rescue CodeRay::Scanners::Scanner::ScanError # this is sometimes raised by pagers; ignore # FIXME: rescue Errno::EPIPE ensure file.close if output_file end end when 'li', 'list' arg = args.first && args.first.downcase if [nil, 's', 'sc', 'scanner', 'scanners'].include? arg puts 'input languages (Scanners):' print_list_of CodeRay::Scanners end if [nil, 'e', 'en', 'enc', 'encoder', 'encoders'].include? arg puts 'output formats (Encoders):' print_list_of CodeRay::Encoders end if [nil, 'st', 'style', 'styles'].include? arg puts 'CSS themes for HTML output (Styles):' print_list_of CodeRay::Styles end if [nil, 'f', 'ft', 'file', 'filetype', 'filetypes'].include? arg puts 'recognized file types:' filetypes = Hash.new { |h, k| h[k] = [] } CodeRay::FileType::TypeFromExt.inject filetypes do |types, (ext, type)| types[type.to_s] << ".#{ext}" types end CodeRay::FileType::TypeFromName.inject filetypes do |types, (name, type)| types[type.to_s] << name types end filetypes.sort.each do |type, exts| puts " #{type}: #{exts.sort_by { |ext| ext.size }.join(', ')}" end end when 'stylesheet', 'style', 'css' puts CodeRay::Encoders[:html]::CSS.new(args.first || :default).stylesheet when 'commands' commands when 'help' help else $stdout = $stderr help puts if subcommand[/\A\w+\z/] puts "Unknown command: #{subcommand}" else puts "File not found: #{subcommand}" end exit 1 end coderay-1.1.3/coderay.gemspec000066400000000000000000000020561366440446100161500ustar00rootroot00000000000000$:.push File.expand_path("../lib", __FILE__) require 'coderay/version' Gem::Specification.new do |s| s.name = 'coderay' if ENV['RELEASE'] s.version = CodeRay::VERSION else s.version = "#{CodeRay::VERSION}.rc#{ENV['RC'] || 1}" end s.authors = ['Kornelius Kalnbach'] s.email = ['murphy@rubychan.de'] s.homepage = 'http://coderay.rubychan.de' s.summary = 'Fast syntax highlighting for selected languages.' s.description = 'Fast and easy syntax highlighting for selected languages, written in Ruby. Comes with RedCloth integration and LOC counter.' s.license = 'MIT' s.platform = Gem::Platform::RUBY s.required_ruby_version = '>= 1.8.6' readme_file = 'README_INDEX.rdoc' s.files = `git ls-files -- lib/* #{readme_file} MIT-LICENSE`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } s.require_paths = ['lib'] s.rdoc_options = '-SNw2', "-m#{readme_file}", '-t CodeRay Documentation' s.extra_rdoc_files = readme_file end coderay-1.1.3/lib/000077500000000000000000000000001366440446100137205ustar00rootroot00000000000000coderay-1.1.3/lib/coderay.rb000066400000000000000000000216611366440446100157010ustar00rootroot00000000000000# encoding: utf-8 # Encoding.default_internal = 'UTF-8' # = CodeRay Library # # CodeRay is a Ruby library for syntax highlighting. # # I try to make CodeRay easy to use and intuitive, but at the same time fully # featured, complete, fast and efficient. # # See README. # # It consists mainly of # * the main engine: CodeRay (Scanners::Scanner, Tokens, Encoders::Encoder) # * the plugin system: PluginHost, Plugin # * the scanners in CodeRay::Scanners # * the encoders in CodeRay::Encoders # * the styles in CodeRay::Styles # # Here's a fancy graphic to light up this gray docu: # # http://cycnus.de/raindark/coderay/scheme.png # # == Documentation # # See CodeRay, Encoders, Scanners, Tokens. # # == Usage # # Remember you need RubyGems to use CodeRay, unless you have it in your load # path. Run Ruby with -rubygems option if required. # # === Highlight Ruby code in a string as html # # require 'coderay' # print CodeRay.scan('puts "Hello, world!"', :ruby).html # # # prints something like this: # puts "Hello, world!" # # # === Highlight C code from a file in a html div # # require 'coderay' # print CodeRay.scan(File.read('ruby.h'), :c).div # print CodeRay.scan_file('ruby.h').html.div # # You can include this div in your page. The used CSS styles can be printed with # # % coderay_stylesheet # # === Highlight without typing too much # # If you are one of the hasty (or lazy, or extremely curious) people, just run this file: # # % ruby -rubygems /path/to/coderay/coderay.rb > example.html # # and look at the file it created in your browser. # # = CodeRay Module # # The CodeRay module provides convenience methods for the engine. # # * The +lang+ and +format+ arguments select Scanner and Encoder to use. These are # simply lower-case symbols, like :python or :html. # * All methods take an optional hash as last parameter, +options+, that is send to # the Encoder / Scanner. # * Input and language are always sorted in this order: +code+, +lang+. # (This is in alphabetical order, if you need a mnemonic ;) # # You should be able to highlight everything you want just using these methods; # so there is no need to dive into CodeRay's deep class hierarchy. # # The examples in the demo directory demonstrate common cases using this interface. # # = Basic Access Ways # # Read this to get a general view what CodeRay provides. # # == Scanning # # Scanning means analysing an input string, splitting it up into Tokens. # Each Token knows about what type it is: string, comment, class name, etc. # # Each +lang+ (language) has its own Scanner; for example, :ruby code is # handled by CodeRay::Scanners::Ruby. # # CodeRay.scan:: Scan a string in a given language into Tokens. # This is the most common method to use. # CodeRay.scan_file:: Scan a file and guess the language using FileType. # # The Tokens object you get from these methods can encode itself; see Tokens. # # == Encoding # # Encoding means compiling Tokens into an output. This can be colored HTML or # LaTeX, a textual statistic or just the number of non-whitespace tokens. # # Each Encoder provides output in a specific +format+, so you select Encoders via # formats like :html or :statistic. # # CodeRay.encode:: Scan and encode a string in a given language. # CodeRay.encode_tokens:: Encode the given tokens. # CodeRay.encode_file:: Scan a file, guess the language using FileType and encode it. # # == All-in-One Encoding # # CodeRay.encode:: Highlight a string with a given input and output format. # # == Instanciating # # You can use an Encoder instance to highlight multiple inputs. This way, the setup # for this Encoder must only be done once. # # CodeRay.encoder:: Create an Encoder instance with format and options. # CodeRay.scanner:: Create an Scanner instance for lang, with '' as default code. # # To make use of CodeRay.scanner, use CodeRay::Scanner::code=. # # The scanning methods provide more flexibility; we recommend to use these. # # == Reusing Scanners and Encoders # # If you want to re-use scanners and encoders (because that is faster), see # CodeRay::Duo for the most convenient (and recommended) interface. module CodeRay $CODERAY_DEBUG ||= false CODERAY_PATH = File.expand_path('../coderay', __FILE__) # Assuming the path is a subpath of lib/coderay/ def self.coderay_path *path File.join CODERAY_PATH, *path end autoload :VERSION, 'coderay/version' # helpers autoload :FileType, coderay_path('helpers', 'file_type') # Tokens autoload :Tokens, coderay_path('tokens') autoload :TokensProxy, coderay_path('tokens_proxy') autoload :TokenKinds, coderay_path('token_kinds') # Plugin system autoload :PluginHost, coderay_path('helpers', 'plugin_host') autoload :Plugin, coderay_path('helpers', 'plugin') # Plugins autoload :Scanners, coderay_path('scanners') autoload :Encoders, coderay_path('encoders') autoload :Styles, coderay_path('styles') # convenience access and reusable Encoder/Scanner pair autoload :Duo, coderay_path('duo') class << self # Scans the given +code+ (a String) with the Scanner for +lang+. # # This is a simple way to use CodeRay. Example: # require 'coderay' # page = CodeRay.scan("puts 'Hello, world!'", :ruby).html # # See also demo/demo_simple. def scan code, lang, options = {}, &block CodeRay::TokensProxy.new code, lang, options, block end # Scans +filename+ (a path to a code file) with the Scanner for +lang+. # # If +lang+ is :auto or omitted, the CodeRay::FileType module is used to # determine it. If it cannot find out what type it is, it uses # CodeRay::Scanners::Text. # # Calls CodeRay.scan. # # Example: # require 'coderay' # page = CodeRay.scan_file('some_c_code.c').html def scan_file filename, lang = :auto, options = {}, &block lang = CodeRay::FileType.fetch filename, :text, true if lang == :auto code = File.read filename scan code, lang, options, &block end # Encode a string. # # This scans +code+ with the the Scanner for +lang+ and then # encodes it with the Encoder for +format+. # +options+ will be passed to the Encoder. # # See CodeRay::Encoder.encode. def encode code, lang, format, options = {} encoder(format, options).encode code, lang, options end # Encode pre-scanned Tokens. # Use this together with CodeRay.scan: # # require 'coderay' # # # Highlight a short Ruby code example in a HTML span # tokens = CodeRay.scan '1 + 2', :ruby # puts CodeRay.encode_tokens(tokens, :span) # def encode_tokens tokens, format, options = {} encoder(format, options).encode_tokens tokens, options end # Encodes +filename+ (a path to a code file) with the Scanner for +lang+. # # See CodeRay.scan_file. # Notice that the second argument is the output +format+, not the input language. # # Example: # require 'coderay' # page = CodeRay.encode_file 'some_c_code.c', :html def encode_file filename, format, options = {} tokens = scan_file filename, :auto, get_scanner_options(options) encode_tokens tokens, format, options end # Highlight a string into a HTML

. # # CSS styles use classes, so you have to include a stylesheet # in your output. # # See encode. def highlight code, lang, options = { :css => :class }, format = :div encode code, lang, format, options end # Highlight a file into a HTML
. # # CSS styles use classes, so you have to include a stylesheet # in your output. # # See encode. def highlight_file filename, options = { :css => :class }, format = :div encode_file filename, format, options end # Finds the Encoder class for +format+ and creates an instance, passing # +options+ to it. # # Example: # require 'coderay' # # stats = CodeRay.encoder(:statistic) # stats.encode("puts 17 + 4\n", :ruby) # # puts '%d out of %d tokens have the kind :integer.' % [ # stats.type_stats[:integer].count, # stats.real_token_count # ] # #-> 2 out of 4 tokens have the kind :integer. def encoder format, options = {} CodeRay::Encoders[format].new options end # Finds the Scanner class for +lang+ and creates an instance, passing # +options+ to it. # # See Scanner.new. def scanner lang, options = {}, &block CodeRay::Scanners[lang].new '', options, &block end # Extract the options for the scanner from the +options+ hash. # # Returns an empty Hash if :scanner_options is not set. # # This is used if a method like CodeRay.encode has to provide options # for Encoder _and_ scanner. def get_scanner_options options options.fetch :scanner_options, {} end end end coderay-1.1.3/lib/coderay/000077500000000000000000000000001366440446100153465ustar00rootroot00000000000000coderay-1.1.3/lib/coderay/duo.rb000066400000000000000000000045071366440446100164700ustar00rootroot00000000000000module CodeRay # = Duo # # A Duo is a convenient way to use CodeRay. You just create a Duo, # giving it a lang (language of the input code) and a format (desired # output format), and call Duo#highlight with the code. # # Duo makes it easy to re-use both scanner and encoder for a repetitive # task. It also provides a very easy interface syntax: # # require 'coderay' # CodeRay::Duo[:python, :div].highlight 'import this' # # Until you want to do uncommon things with CodeRay, I recommend to use # this method, since it takes care of everything. class Duo attr_accessor :lang, :format, :options # Create a new Duo, holding a lang and a format to highlight code. # # simple: # CodeRay::Duo[:ruby, :html].highlight 'bla 42' # # with options: # CodeRay::Duo[:ruby, :html, :hint => :debug].highlight '????::??' # # alternative syntax without options: # CodeRay::Duo[:ruby => :statistic].encode 'class << self; end' # # alternative syntax with options: # CodeRay::Duo[{ :ruby => :statistic }, :do => :something].encode 'abc' # # The options are forwarded to scanner and encoder # (see CodeRay.get_scanner_options). def initialize lang = nil, format = nil, options = {} if format.nil? && lang.is_a?(Hash) && lang.size == 1 @lang = lang.keys.first @format = lang[@lang] else @lang = lang @format = format end @options = options end class << self # To allow calls like Duo[:ruby, :html].highlight. alias [] new end # The scanner of the duo. Only created once. def scanner @scanner ||= CodeRay.scanner @lang, CodeRay.get_scanner_options(@options) end # The encoder of the duo. Only created once. def encoder @encoder ||= CodeRay.encoder @format, @options end # Tokenize and highlight the code using +scanner+ and +encoder+. def encode code, options = {} options = @options.merge options encoder.encode(code, @lang, options) end alias highlight encode # Allows to use Duo like a proc object: # # CodeRay::Duo[:python => :yaml].call(code) # # or, in Ruby 1.9 and later: # # CodeRay::Duo[:python => :yaml].(code) alias call encode end end coderay-1.1.3/lib/coderay/encoders.rb000066400000000000000000000010331366440446100174720ustar00rootroot00000000000000module CodeRay # This module holds the Encoder class and its subclasses. # For example, the HTML encoder is named CodeRay::Encoders::HTML # can be found in coderay/encoders/html. # # Encoders also provides methods and constants for the register # mechanism and the [] method that returns the Encoder class # belonging to the given format. module Encoders extend PluginHost plugin_path File.dirname(__FILE__), 'encoders' autoload :Encoder, CodeRay.coderay_path('encoders', 'encoder') end end coderay-1.1.3/lib/coderay/encoders/000077500000000000000000000000001366440446100171505ustar00rootroot00000000000000coderay-1.1.3/lib/coderay/encoders/_map.rb000066400000000000000000000006221366440446100204110ustar00rootroot00000000000000module CodeRay module Encoders map \ :loc => :lines_of_code, :plain => :text, :plaintext => :text, :remove_comments => :comment_filter, :stats => :statistic, :term => :terminal, :tty => :terminal, :yml => :yaml # No default because Tokens#nonsense should raise NoMethodError. end end coderay-1.1.3/lib/coderay/encoders/comment_filter.rb000066400000000000000000000007751366440446100225150ustar00rootroot00000000000000module CodeRay module Encoders load :token_kind_filter # A simple Filter that removes all tokens of the :comment kind. # # Alias: +remove_comments+ # # Usage: # CodeRay.scan('print # foo', :ruby).comment_filter.text # #-> "print " # # See also: TokenKindFilter, LinesOfCode class CommentFilter < TokenKindFilter register_for :comment_filter DEFAULT_OPTIONS = superclass::DEFAULT_OPTIONS.merge \ :exclude => [:comment, :docstring] end end end coderay-1.1.3/lib/coderay/encoders/count.rb000066400000000000000000000010561366440446100206270ustar00rootroot00000000000000module CodeRay module Encoders # Returns the number of tokens. # # Text and block tokens are counted. class Count < Encoder register_for :count protected def setup options super @count = 0 end def finish options output @count end public def text_token text, kind @count += 1 end def begin_group kind @count += 1 end alias end_group begin_group alias begin_line begin_group alias end_line begin_group end end end coderay-1.1.3/lib/coderay/encoders/debug.rb000066400000000000000000000016531366440446100205700ustar00rootroot00000000000000module CodeRay module Encoders # = Debug Encoder # # Fast encoder producing simple debug output. # # It is readable and diff-able and is used for testing. # # You cannot fully restore the tokens information from the # output, because consecutive :space tokens are merged. # # See also: Scanners::Debug class Debug < Encoder register_for :debug FILE_EXTENSION = 'raydebug' def text_token text, kind if kind == :space @out << text else text = text.gsub('\\', '\\\\\\\\') if text.index('\\') text = text.gsub(')', '\\\\)') if text.index(')') @out << "#{kind}(#{text})" end end def begin_group kind @out << "#{kind}<" end def end_group kind @out << '>' end def begin_line kind @out << "#{kind}[" end def end_line kind @out << ']' end end end end coderay-1.1.3/lib/coderay/encoders/debug_lint.rb000066400000000000000000000026641366440446100216210ustar00rootroot00000000000000module CodeRay module Encoders load :lint # = Debug Lint Encoder # # Debug encoder with additional checks for: # # - empty tokens # - incorrect nesting # # It will raise an InvalidTokenStream exception when any of the above occurs. # # See also: Encoders::Debug class DebugLint < Debug register_for :debug_lint def text_token text, kind raise Lint::EmptyToken, 'empty token for %p' % [kind] if text.empty? raise Lint::UnknownTokenKind, 'unknown token kind %p (text was %p)' % [kind, text] unless TokenKinds.has_key? kind super end def begin_group kind @opened << kind super end def end_group kind raise Lint::IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_group)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind @opened.pop super end def begin_line kind @opened << kind super end def end_line kind raise Lint::IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_line)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind @opened.pop super end protected def setup options super @opened = [] end def finish options raise 'Some tokens still open at end of token stream: %p' % [@opened] unless @opened.empty? super end end end end coderay-1.1.3/lib/coderay/encoders/div.rb000066400000000000000000000006521366440446100202620ustar00rootroot00000000000000module CodeRay module Encoders load :html # Wraps HTML output into a DIV element, using inline styles by default. # # See Encoders::HTML for available options. class Div < HTML FILE_EXTENSION = 'div.html' register_for :div DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \ :css => :style, :wrap => :div, :line_numbers => false end end end coderay-1.1.3/lib/coderay/encoders/encoder.rb000066400000000000000000000130111366440446100211100ustar00rootroot00000000000000module CodeRay module Encoders # = Encoder # # The Encoder base class. Together with Scanner and # Tokens, it forms the highlighting triad. # # Encoder instances take a Tokens object and do something with it. # # The most common Encoder is surely the HTML encoder # (CodeRay::Encoders::HTML). It highlights the code in a colorful # html page. # If you want the highlighted code in a div or a span instead, # use its subclasses Div and Span. class Encoder extend Plugin plugin_host Encoders class << self # If FILE_EXTENSION isn't defined, this method returns the # downcase class name instead. def const_missing sym if sym == :FILE_EXTENSION (defined?(@plugin_id) && @plugin_id || name[/\w+$/].downcase).to_s else super end end # The default file extension for output file of this encoder class. def file_extension self::FILE_EXTENSION end end # Subclasses are to store their default options in this constant. DEFAULT_OPTIONS = { } # The options you gave the Encoder at creating. attr_accessor :options, :scanner # Creates a new Encoder. # +options+ is saved and used for all encode operations, as long # as you don't overwrite it there by passing additional options. # # Encoder objects provide three encode methods: # - encode simply takes a +code+ string and a +lang+ # - encode_tokens expects a +tokens+ object instead # # Each method has an optional +options+ parameter. These are # added to the options you passed at creation. def initialize options = {} @options = self.class::DEFAULT_OPTIONS.merge options @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = false end # Encode a Tokens object. def encode_tokens tokens, options = {} options = @options.merge options @scanner = tokens.scanner if tokens.respond_to? :scanner setup options compile tokens, options finish options end # Encode the given +code+ using the Scanner for +lang+. def encode code, lang, options = {} options = @options.merge options @scanner = Scanners[lang].new code, CodeRay.get_scanner_options(options).update(:tokens => self) setup options @scanner.tokenize finish options end # You can use highlight instead of encode, if that seems # more clear to you. alias highlight encode # The default file extension for this encoder. def file_extension self.class.file_extension end def << token unless @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN warn 'Using old Tokens#<< interface.' @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = true end self.token(*token) end # Called with +content+ and +kind+ of the currently scanned token. # For simple scanners, it's enougth to implement this method. # # By default, it calls text_token, begin_group, end_group, begin_line, # or end_line, depending on the +content+. def token content, kind case content when String text_token content, kind when :begin_group begin_group kind when :end_group end_group kind when :begin_line begin_line kind when :end_line end_line kind else raise ArgumentError, 'Unknown token content type: %p, kind = %p' % [content, kind] end end # Called for each text token ([text, kind]), where text is a String. def text_token text, kind @out << text end # Starts a token group with the given +kind+. def begin_group kind end # Ends a token group with the given +kind+. def end_group kind end # Starts a new line token group with the given +kind+. def begin_line kind end # Ends a new line token group with the given +kind+. def end_line kind end protected # Called with merged options before encoding starts. # Sets @out to an empty string. # # See the HTML Encoder for an example of option caching. def setup options @out = get_output(options) end def get_output options options[:out] || ''.dup end # Append data.to_s to the output. Returns the argument. def output data @out << data.to_s data end # Called with merged options after encoding starts. # The return value is the result of encoding, typically @out. def finish options @out end # Do the encoding. # # The already created +tokens+ object must be used; it must be a # Tokens object. def compile tokens, options = {} content = nil for item in tokens if item.is_a? Array raise ArgumentError, 'Two-element array tokens are no longer supported.' end if content token content, item content = nil else content = item end end raise 'odd number list for Tokens' if content end alias tokens compile public :tokens end end end coderay-1.1.3/lib/coderay/encoders/filter.rb000066400000000000000000000020451366440446100207630ustar00rootroot00000000000000module CodeRay module Encoders # A Filter encoder has another Tokens instance as output. # It can be subclass to select, remove, or modify tokens in the stream. # # Subclasses of Filter are called "Filters" and can be chained. # # == Options # # === :tokens # # The Tokens object which will receive the output. # # Default: Tokens.new # # See also: TokenKindFilter class Filter < Encoder register_for :filter protected def setup options super @tokens = options[:tokens] || Tokens.new end def finish options output @tokens end public def text_token text, kind # :nodoc: @tokens.text_token text, kind end def begin_group kind # :nodoc: @tokens.begin_group kind end def begin_line kind # :nodoc: @tokens.begin_line kind end def end_group kind # :nodoc: @tokens.end_group kind end def end_line kind # :nodoc: @tokens.end_line kind end end end end coderay-1.1.3/lib/coderay/encoders/html.rb000066400000000000000000000220011366440446100204340ustar00rootroot00000000000000require 'set' module CodeRay module Encoders # = HTML Encoder # # This is CodeRay's most important highlighter: # It provides save, fast XHTML generation and CSS support. # # == Usage # # require 'coderay' # puts CodeRay.scan('Some /code/', :ruby).html #-> a HTML page # puts CodeRay.scan('Some /code/', :ruby).html(:wrap => :span) # #-> Some /code/ # puts CodeRay.scan('Some /code/', :ruby).span #-> the same # # puts CodeRay.scan('Some code', :ruby).html( # :wrap => nil, # :line_numbers => :inline, # :css => :style # ) # # == Options # # === :tab_width # Convert \t characters to +n+ spaces (a number or false.) # false will keep tab characters untouched. # # Default: 8 # # === :css # How to include the styles; can be :class or :style. # # Default: :class # # === :wrap # Wrap in :page, :div, :span or nil. # # You can also use Encoders::Div and Encoders::Span. # # Default: nil # # === :title # # The title of the HTML page (works only when :wrap is set to :page.) # # Default: 'CodeRay output' # # === :break_lines # # Split multiline blocks at line breaks. # Forced to true if :line_numbers option is set to :inline. # # Default: false # # === :line_numbers # Include line numbers in :table, :inline, or nil (no line numbers) # # Default: nil # # === :line_number_anchors # Adds anchors and links to the line numbers. Can be false (off), true (on), # or a prefix string that will be prepended to the anchor name. # # The prefix must consist only of letters, digits, and underscores. # # Default: true, default prefix name: "line" # # === :line_number_start # Where to start with line number counting. # # Default: 1 # # === :bold_every # Make every +n+-th number appear bold. # # Default: 10 # # === :highlight_lines # # Highlights certain line numbers. # Can be any Enumerable, typically just an Array or Range, of numbers. # # Bolding is deactivated when :highlight_lines is set. It only makes sense # in combination with :line_numbers. # # Default: nil # # === :hint # Include some information into the output using the title attribute. # Can be :info (show token kind on mouse-over), :info_long (with full path) # or :debug (via inspect). # # Default: false class HTML < Encoder register_for :html FILE_EXTENSION = 'snippet.html' DEFAULT_OPTIONS = { :tab_width => 8, :css => :class, :style => :alpha, :wrap => nil, :title => 'CodeRay output', :break_lines => false, :line_numbers => nil, :line_number_anchors => 'n', :line_number_start => 1, :bold_every => 10, :highlight_lines => nil, :hint => false, } autoload :Output, CodeRay.coderay_path('encoders', 'html', 'output') autoload :CSS, CodeRay.coderay_path('encoders', 'html', 'css') autoload :Numbering, CodeRay.coderay_path('encoders', 'html', 'numbering') attr_reader :css protected def self.make_html_escape_hash { '&' => '&', '"' => '"', '>' => '>', '<' => '<', # "\t" => will be set to ' ' * options[:tab_width] during setup }.tap do |hash| # Escape ASCII control codes except \x9 == \t and \xA == \n. (Array(0x00..0x8) + Array(0xB..0x1F)).each { |invalid| hash[invalid.chr] = ' ' } end end HTML_ESCAPE = make_html_escape_hash HTML_ESCAPE_PATTERN = /[\t"&><\0-\x8\xB-\x1F]/ TOKEN_KIND_TO_INFO = Hash.new do |h, kind| h[kind] = kind.to_s.gsub(/_/, ' ').gsub(/\b\w/) { $&.capitalize } end TRANSPARENT_TOKEN_KINDS = Set[ :delimiter, :modifier, :content, :escape, :inline_delimiter, ] # Generate a hint about the given +kinds+ in a +hint+ style. # # +hint+ may be :info, :info_long or :debug. def self.token_path_to_hint hint, kinds kinds = Array kinds title = case hint when :info kinds = kinds[1..-1] if TRANSPARENT_TOKEN_KINDS.include? kinds.first TOKEN_KIND_TO_INFO[kinds.first] when :info_long kinds.reverse.map { |kind| TOKEN_KIND_TO_INFO[kind] }.join('/') when :debug kinds.inspect end title ? " title=\"#{title}\"" : '' end def setup options super check_options! options if options[:wrap] || options[:line_numbers] @real_out = @out @out = ''.dup end @break_lines = (options[:break_lines] == true) @HTML_ESCAPE = HTML_ESCAPE.merge("\t" => options[:tab_width] ? ' ' * options[:tab_width] : "\t") @opened = [] @last_opened = nil @css = CSS.new options[:style] @span_for_kinds = make_span_for_kinds(options[:css], options[:hint]) @set_last_opened = options[:hint] || options[:css] == :style end def finish options unless @opened.empty? @out << '' while @opened.pop @last_opened = nil end if @out.respond_to? :to_str @out.extend Output @out.css = @css if options[:line_numbers] Numbering.number! @out, options[:line_numbers], options end @out.wrap! options[:wrap] @out.apply_title! options[:title] end if defined?(@real_out) && @real_out @real_out << @out @out = @real_out end super end public def text_token text, kind style = @span_for_kinds[@last_opened ? [kind, *@opened] : kind] text = text.gsub(/#{HTML_ESCAPE_PATTERN}/o) { |m| @HTML_ESCAPE[m] } if text =~ /#{HTML_ESCAPE_PATTERN}/o text = break_lines(text, style) if @break_lines && (style || @opened.size > 0) && text.index("\n") if style @out << style << text << '' else @out << text end end # token groups, eg. strings def begin_group kind @out << (@span_for_kinds[@last_opened ? [kind, *@opened] : kind] || '') @opened << kind @last_opened = kind if @set_last_opened end def end_group kind check_group_nesting 'token group', kind if $CODERAY_DEBUG close_span end # whole lines to be highlighted, eg. a deleted line in a diff def begin_line kind if style = @span_for_kinds[@last_opened ? [kind, *@opened] : kind] if style['class="'] @out << style.sub('class="', 'class="line ') else @out << style.sub('>', ' class="line">') end else @out << '' end @opened << kind @last_opened = kind if @options[:css] == :style end def end_line kind check_group_nesting 'line', kind if $CODERAY_DEBUG close_span end protected def check_options! options unless [false, nil, :debug, :info, :info_long].include? options[:hint] raise ArgumentError, "Unknown value %p for :hint; expected :info, :info_long, :debug, false, or nil." % [options[:hint]] end unless [:class, :style].include? options[:css] raise ArgumentError, 'Unknown value %p for :css.' % [options[:css]] end options[:break_lines] = true if options[:line_numbers] == :inline end def css_class_for_kinds kinds TokenKinds[kinds.is_a?(Symbol) ? kinds : kinds.first] end def style_for_kinds kinds css_classes = kinds.is_a?(Array) ? kinds.map { |c| TokenKinds[c] } : [TokenKinds[kinds]] @css.get_style_for_css_classes css_classes end def make_span_for_kinds method, hint Hash.new do |h, kinds| begin css_class = css_class_for_kinds(kinds) title = HTML.token_path_to_hint hint, kinds if hint if css_class || title if method == :style style = style_for_kinds(kinds) "" else "" end end end.tap do |span| h.clear if h.size >= 100 h[kinds] = span end end end def check_group_nesting name, kind if @opened.empty? || @opened.last != kind warn "Malformed token stream: Trying to close a #{name} (%p) that is not open. Open are: %p." % [kind, @opened[1..-1]] end end def break_lines text, style reopen = ''.dup @opened.each_with_index do |kind, index| reopen << (@span_for_kinds[index > 0 ? [kind, *@opened[0...index]] : kind] || '') end text.gsub("\n", "#{'' * @opened.size}#{'' if style}\n#{reopen}#{style}") end def close_span if @opened.pop @out << '' @last_opened = @opened.last if @last_opened end end end end end coderay-1.1.3/lib/coderay/encoders/html/000077500000000000000000000000001366440446100201145ustar00rootroot00000000000000coderay-1.1.3/lib/coderay/encoders/html/css.rb000066400000000000000000000030761366440446100212370ustar00rootroot00000000000000module CodeRay module Encoders class HTML class CSS # :nodoc: attr :stylesheet def CSS.load_stylesheet style = nil CodeRay::Styles[style] end def initialize style = :default @styles = Hash.new style = CSS.load_stylesheet style @stylesheet = [ style::CSS_MAIN_STYLES, style::TOKEN_COLORS.gsub(/^(?!$)/, '.CodeRay ') ].join("\n") parse style::TOKEN_COLORS end def get_style_for_css_classes css_classes cl = @styles[css_classes.first] return '' unless cl style = '' 1.upto css_classes.size do |offset| break if style = cl[css_classes[offset .. -1]] end # warn 'Style not found: %p' % [styles] if style.empty? return style end private CSS_CLASS_PATTERN = / ( # $1 = selectors (?: (?: \s* \. [-\w]+ )+ \s* ,? )+ ) \s* \{ \s* ( [^\}]+ )? # $2 = style \s* \} \s* | ( [^\n]+ ) # $3 = error /mx def parse stylesheet stylesheet.scan CSS_CLASS_PATTERN do |selectors, style, error| raise "CSS parse error: '#{error.inspect}' not recognized" if error for selector in selectors.split(',') classes = selector.scan(/[-\w]+/) cl = classes.pop @styles[cl] ||= Hash.new @styles[cl][classes] = style.to_s.strip.delete(' ').chomp(';') end end end end end end end coderay-1.1.3/lib/coderay/encoders/html/numbering.rb000066400000000000000000000070021366440446100224260ustar00rootroot00000000000000module CodeRay module Encoders class HTML module Numbering # :nodoc: def self.number! output, mode = :table, options = {} return self unless mode options = DEFAULT_OPTIONS.merge options start = options[:line_number_start] unless start.is_a? Integer raise ArgumentError, "Invalid value %p for :line_number_start; Integer expected." % start end anchor_prefix = options[:line_number_anchors] anchor_prefix = 'line' if anchor_prefix == true anchor_prefix = anchor_prefix.to_s[/[\w-]+/] if anchor_prefix anchoring = if anchor_prefix proc do |line| line = line.to_s anchor = anchor_prefix + line "#{line}" end else :to_s.to_proc end bold_every = options[:bold_every] highlight_lines = options[:highlight_lines] bolding = if bold_every == false && highlight_lines == nil anchoring elsif highlight_lines.is_a? Enumerable highlight_lines = highlight_lines.to_set proc do |line| if highlight_lines.include? line "#{anchoring[line]}" # highlighted line numbers in bold else anchoring[line] end end elsif bold_every.is_a? Integer raise ArgumentError, ":bolding can't be 0." if bold_every == 0 proc do |line| if line % bold_every == 0 "#{anchoring[line]}" # every bold_every-th number in bold else anchoring[line] end end else raise ArgumentError, 'Invalid value %p for :bolding; false or Integer expected.' % bold_every end if position_of_last_newline = output.rindex(RUBY_VERSION >= '1.9' ? /\n/ : ?\n) after_last_newline = output[position_of_last_newline + 1 .. -1] ends_with_newline = after_last_newline[/\A(?:<\/span>)*\z/] if ends_with_newline line_count = output.count("\n") else line_count = output.count("\n") + 1 end else line_count = 1 end case mode when :inline max_width = (start + line_count).to_s.size line_number = start output.gsub!(/^.*$\n?/) do |line| line_number_text = bolding.call line_number indent = ' ' * (max_width - line_number.to_s.size) line_number += 1 "#{indent}#{line_number_text}#{line}" end when :table line_numbers = (start ... start + line_count).map(&bolding).join("\n") line_numbers << "\n" line_numbers_table_template = Output::TABLE.apply('LINE_NUMBERS', line_numbers) output.gsub!(/<\/div>\n/, '
') output.wrap_in! line_numbers_table_template output.wrapped_in = :div when :list raise NotImplementedError, 'The :list option is no longer available. Use :table.' else raise ArgumentError, 'Unknown value %p for mode: expected one of %p' % [mode, [:table, :inline]] end output end end end end end coderay-1.1.3/lib/coderay/encoders/html/output.rb000066400000000000000000000075071366440446100220120ustar00rootroot00000000000000module CodeRay module Encoders class HTML # This module is included in the output String of the HTML Encoder. # # It provides methods like wrap, div, page etc. # # Remember to use #clone instead of #dup to keep the modules the object was # extended with. # # TODO: Rewrite this without monkey patching. module Output attr_accessor :css class << self # Raises an exception if an object that doesn't respond to to_str is extended by Output, # to prevent users from misuse. Use Module#remove_method to disable. def extended o # :nodoc: warn "The Output module is intended to extend instances of String, not #{o.class}." unless o.respond_to? :to_str end def make_stylesheet css, in_tag = false # :nodoc: sheet = css.stylesheet sheet = <<-'CSS' if in_tag CSS sheet end def page_template_for_css css # :nodoc: sheet = make_stylesheet css PAGE.apply 'CSS', sheet end end def wrapped_in? element wrapped_in == element end def wrapped_in @wrapped_in ||= nil end attr_writer :wrapped_in def wrap_in! template Template.wrap! self, template, 'CONTENT' self end def apply_title! title self.sub!(/()(<\/title>)/) { $1 + title + $2 } self end def wrap! element, *args return self if not element or element == wrapped_in case element when :div raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil wrap_in! DIV when :span raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil wrap_in! SPAN when :page wrap! :div if wrapped_in? nil raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? :div wrap_in! Output.page_template_for_css(@css) if args.first.is_a?(Hash) && title = args.first[:title] apply_title! title end self else raise "Unknown value %p for :wrap" % element end @wrapped_in = element self end def stylesheet in_tag = false Output.make_stylesheet @css, in_tag end #-- don't include the templates in docu class Template < String # :nodoc: def self.wrap! str, template, target target = Regexp.new(Regexp.escape("<%#{target}%>")) if template =~ target str[0,0] = $` str << $' else raise "Template target <%%%p%%> not found" % target end end def apply target, replacement target = Regexp.new(Regexp.escape("<%#{target}%>")) if self =~ target Template.new($` + replacement + $') else raise "Template target <%%%p%%> not found" % target end end end SPAN = Template.new '<span class="CodeRay"><%CONTENT%></span>' DIV = Template.new <<-DIV <div class="CodeRay"> <div class="code"><pre><%CONTENT%></pre></div> </div> DIV TABLE = Template.new <<-TABLE <table class="CodeRay"><tr> <td class="line-numbers"><pre><%LINE_NUMBERS%></pre></td> <td class="code"><pre><%CONTENT%></pre></td> </tr></table> TABLE PAGE = Template.new <<-PAGE <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> <%CONTENT%> PAGE end end end end coderay-1.1.3/lib/coderay/encoders/json.rb000066400000000000000000000034471366440446100204560ustar00rootroot00000000000000module CodeRay module Encoders # A simple JSON Encoder. # # Example: # CodeRay.scan('puts "Hello world!"', :ruby).json # yields # [ # {"type"=>"text", "text"=>"puts", "kind"=>"ident"}, # {"type"=>"text", "text"=>" ", "kind"=>"space"}, # {"type"=>"block", "action"=>"open", "kind"=>"string"}, # {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"}, # {"type"=>"text", "text"=>"Hello world!", "kind"=>"content"}, # {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"}, # {"type"=>"block", "action"=>"close", "kind"=>"string"}, # ] class JSON < Encoder begin require 'json' rescue LoadError begin require 'rubygems' unless defined? Gem gem 'json' require 'json' rescue LoadError $stderr.puts "The JSON encoder needs the JSON library.\n" \ "Please gem install json." raise end end register_for :json FILE_EXTENSION = 'json' protected def setup options super @first = true @out << '[' end def finish options @out << ']' end def append data if @first @first = false else @out << ',' end @out << data.to_json end public def text_token text, kind append :type => 'text', :text => text, :kind => kind end def begin_group kind append :type => 'block', :action => 'open', :kind => kind end def end_group kind append :type => 'block', :action => 'close', :kind => kind end def begin_line kind append :type => 'block', :action => 'begin_line', :kind => kind end def end_line kind append :type => 'block', :action => 'end_line', :kind => kind end end end end coderay-1.1.3/lib/coderay/encoders/lines_of_code.rb000066400000000000000000000021511366440446100222640ustar00rootroot00000000000000module CodeRay module Encoders # Counts the LoC (Lines of Code). Returns an Integer >= 0. # # Alias: +loc+ # # Everything that is not comment, markup, doctype/shebang, or an empty line, # is considered to be code. # # For example, # * HTML files not containing JavaScript have 0 LoC # * in a Java class without comments, LoC is the number of non-empty lines # # A Scanner class should define the token kinds that are not code in the # KINDS_NOT_LOC constant, which defaults to [:comment, :doctype]. class LinesOfCode < TokenKindFilter register_for :lines_of_code NON_EMPTY_LINE = /^\s*\S.*$/ protected def setup options if scanner kinds_not_loc = scanner.class::KINDS_NOT_LOC else warn "Tokens have no associated scanner, counting all nonempty lines." if $VERBOSE kinds_not_loc = CodeRay::Scanners::Scanner::KINDS_NOT_LOC end options[:exclude] = kinds_not_loc super options end def finish options output @tokens.text.scan(NON_EMPTY_LINE).size end end end end coderay-1.1.3/lib/coderay/encoders/lint.rb000066400000000000000000000030051366440446100204410ustar00rootroot00000000000000module CodeRay module Encoders # = Lint Encoder # # Checks for: # # - empty tokens # - incorrect nesting # # It will raise an InvalidTokenStream exception when any of the above occurs. # # See also: Encoders::DebugLint class Lint < Debug register_for :lint InvalidTokenStream = Class.new StandardError EmptyToken = Class.new InvalidTokenStream UnknownTokenKind = Class.new InvalidTokenStream IncorrectTokenGroupNesting = Class.new InvalidTokenStream def text_token text, kind raise EmptyToken, 'empty token for %p' % [kind] if text.empty? raise UnknownTokenKind, 'unknown token kind %p (text was %p)' % [kind, text] unless TokenKinds.has_key? kind end def begin_group kind @opened << kind end def end_group kind raise IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_group)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind @opened.pop end def begin_line kind @opened << kind end def end_line kind raise IncorrectTokenGroupNesting, 'We are inside %s, not %p (end_line)' % [@opened.reverse.map(&:inspect).join(' < '), kind] if @opened.last != kind @opened.pop end protected def setup options @opened = [] end def finish options raise 'Some tokens still open at end of token stream: %p' % [@opened] unless @opened.empty? end end end end coderay-1.1.3/lib/coderay/encoders/null.rb000066400000000000000000000003561366440446100204530ustar00rootroot00000000000000module CodeRay module Encoders # = Null Encoder # # Does nothing and returns an empty string. class Null < Encoder register_for :null def text_token text, kind # do nothing end end end end coderay-1.1.3/lib/coderay/encoders/page.rb000066400000000000000000000007161366440446100204150ustar00rootroot00000000000000module CodeRay module Encoders load :html # Wraps the output into a HTML page, using CSS classes and # line numbers in the table format by default. # # See Encoders::HTML for available options. class Page < HTML FILE_EXTENSION = 'html' register_for :page DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \ :css => :class, :wrap => :page, :line_numbers => :table end end end coderay-1.1.3/lib/coderay/encoders/span.rb000066400000000000000000000006571366440446100204460ustar00rootroot00000000000000module CodeRay module Encoders load :html # Wraps HTML output into a SPAN element, using inline styles by default. # # See Encoders::HTML for available options. class Span < HTML FILE_EXTENSION = 'span.html' register_for :span DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \ :css => :style, :wrap => :span, :line_numbers => false end end end coderay-1.1.3/lib/coderay/encoders/statistic.rb000066400000000000000000000040751366440446100215120ustar00rootroot00000000000000module CodeRay module Encoders # Makes a statistic for the given tokens. # # Alias: +stats+ class Statistic < Encoder register_for :statistic attr_reader :type_stats, :real_token_count # :nodoc: TypeStats = Struct.new :count, :size # :nodoc: protected def setup options super @type_stats = Hash.new { |h, k| h[k] = TypeStats.new 0, 0 } @real_token_count = 0 end STATS = <<-STATS # :nodoc: Code Statistics Tokens %8d Non-Whitespace %8d Bytes Total %8d Token Types (%d): type count ratio size (average) ------------------------------------------------------------- %s STATS TOKEN_TYPES_ROW = <<-TKR # :nodoc: %-20s %8d %6.2f %% %5.1f TKR def finish options all = @type_stats['TOTAL'] all_count, all_size = all.count, all.size @type_stats.each do |type, stat| stat.size /= stat.count.to_f end types_stats = @type_stats.sort_by { |k, v| [-v.count, k.to_s] }.map do |k, v| TOKEN_TYPES_ROW % [k, v.count, 100.0 * v.count / all_count, v.size] end.join @out << STATS % [ all_count, @real_token_count, all_size, @type_stats.delete_if { |k, v| k.is_a? String }.size, types_stats ] super end public def text_token text, kind @real_token_count += 1 unless kind == :space @type_stats[kind].count += 1 @type_stats[kind].size += text.size @type_stats['TOTAL'].size += text.size @type_stats['TOTAL'].count += 1 end def begin_group kind block_token ':begin_group', kind end def end_group kind block_token ':end_group', kind end def begin_line kind block_token ':begin_line', kind end def end_line kind block_token ':end_line', kind end def block_token action, kind @type_stats['TOTAL'].count += 1 @type_stats[action].count += 1 @type_stats[kind].count += 1 end end end end coderay-1.1.3/lib/coderay/encoders/terminal.rb000066400000000000000000000117161366440446100213160ustar00rootroot00000000000000module CodeRay module Encoders # Outputs code highlighted for a color terminal. # # Note: This encoder is in beta. It currently doesn't use the Styles. # # Alias: +term+ # # == Authors & License # # By Rob Aldred (http://robaldred.co.uk) # # Based on idea by Nathan Weizenbaum (http://nex-3.com) # # MIT License (http://www.opensource.org/licenses/mit-license.php) class Terminal < Encoder register_for :terminal TOKEN_COLORS = { :debug => "\e[1;37;44m", :annotation => "\e[34m", :attribute_name => "\e[35m", :attribute_value => "\e[31m", :binary => { :self => "\e[31m", :char => "\e[1;31m", :delimiter => "\e[1;31m", }, :char => { :self => "\e[35m", :delimiter => "\e[1;35m" }, :class => "\e[1;35;4m", :class_variable => "\e[36m", :color => "\e[32m", :comment => { :self => "\e[1;30m", :char => "\e[37m", :delimiter => "\e[37m", }, :constant => "\e[1;34;4m", :decorator => "\e[35m", :definition => "\e[1;33m", :directive => "\e[33m", :docstring => "\e[31m", :doctype => "\e[1;34m", :done => "\e[1;30;2m", :entity => "\e[31m", :error => "\e[1;37;41m", :exception => "\e[1;31m", :float => "\e[1;35m", :function => "\e[1;34m", :global_variable => "\e[1;32m", :hex => "\e[1;36m", :id => "\e[1;34m", :include => "\e[31m", :integer => "\e[1;34m", :imaginary => "\e[1;34m", :important => "\e[1;31m", :key => { :self => "\e[35m", :char => "\e[1;35m", :delimiter => "\e[1;35m", }, :keyword => "\e[32m", :label => "\e[1;33m", :local_variable => "\e[33m", :namespace => "\e[1;35m", :octal => "\e[1;34m", :predefined => "\e[36m", :predefined_constant => "\e[1;36m", :predefined_type => "\e[1;32m", :preprocessor => "\e[1;36m", :pseudo_class => "\e[1;34m", :regexp => { :self => "\e[35m", :delimiter => "\e[1;35m", :modifier => "\e[35m", :char => "\e[1;35m", }, :reserved => "\e[32m", :shell => { :self => "\e[33m", :char => "\e[1;33m", :delimiter => "\e[1;33m", :escape => "\e[1;33m", }, :string => { :self => "\e[31m", :modifier => "\e[1;31m", :char => "\e[1;35m", :delimiter => "\e[1;31m", :escape => "\e[1;31m", }, :symbol => { :self => "\e[33m", :delimiter => "\e[1;33m", }, :tag => "\e[32m", :type => "\e[1;34m", :value => "\e[36m", :variable => "\e[34m", :insert => { :self => "\e[42m", :insert => "\e[1;32;42m", :eyecatcher => "\e[102m", }, :delete => { :self => "\e[41m", :delete => "\e[1;31;41m", :eyecatcher => "\e[101m", }, :change => { :self => "\e[44m", :change => "\e[37;44m", }, :head => { :self => "\e[45m", :filename => "\e[37;45m" }, } TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved] TOKEN_COLORS[:method] = TOKEN_COLORS[:function] TOKEN_COLORS[:escape] = TOKEN_COLORS[:delimiter] protected def setup(options) super @opened = [] @color_scopes = [TOKEN_COLORS] end public def text_token text, kind if color = @color_scopes.last[kind] color = color[:self] if color.is_a? Hash @out << color @out << (text.index("\n") ? text.gsub("\n", "\e[0m\n" + color) : text) @out << "\e[0m" if outer_color = @color_scopes.last[:self] @out << outer_color end else @out << text end end def begin_group kind @opened << kind @out << open_token(kind) end alias begin_line begin_group def end_group kind if @opened.pop @color_scopes.pop @out << "\e[0m" if outer_color = @color_scopes.last[:self] @out << outer_color end end end def end_line kind @out << (@line_filler ||= "\t" * 100) end_group kind end private def open_token kind if color = @color_scopes.last[kind] if color.is_a? Hash @color_scopes << color color[:self] else @color_scopes << @color_scopes.last color end else @color_scopes << @color_scopes.last '' end end end end end coderay-1.1.3/lib/coderay/encoders/text.rb000066400000000000000000000013411366440446100204600ustar00rootroot00000000000000module CodeRay module Encoders # Concats the tokens into a single string, resulting in the original # code string if no tokens were removed. # # Alias: +plain+, +plaintext+ # # == Options # # === :separator # A separator string to join the tokens. # # Default: empty String class Text < Encoder register_for :text FILE_EXTENSION = 'txt' DEFAULT_OPTIONS = { :separator => nil } def text_token text, kind super if @first @first = false else @out << @sep end if @sep end protected def setup options super @first = true @sep = options[:separator] end end end end coderay-1.1.3/lib/coderay/encoders/token_kind_filter.rb000066400000000000000000000046271366440446100232000ustar00rootroot00000000000000module CodeRay module Encoders load :filter # A Filter that selects tokens based on their token kind. # # == Options # # === :exclude # # One or many symbols (in an Array) which shall be excluded. # # Default: [] # # === :include # # One or many symbols (in an array) which shall be included. # # Default: :all, which means all tokens are included. # # Exclusion wins over inclusion. # # See also: CommentFilter class TokenKindFilter < Filter register_for :token_kind_filter DEFAULT_OPTIONS = { :exclude => [], :include => :all } protected def setup options super @group_excluded = false @exclude = options[:exclude] @exclude = Array(@exclude) unless @exclude == :all @include = options[:include] @include = Array(@include) unless @include == :all end def include_text_token? text, kind include_group? kind end def include_group? kind (@include == :all || @include.include?(kind)) && !(@exclude == :all || @exclude.include?(kind)) end public # Add the token to the output stream if +kind+ matches the conditions. def text_token text, kind super if !@group_excluded && include_text_token?(text, kind) end # Add the token group to the output stream if +kind+ matches the # conditions. # # If it does not, all tokens inside the group are excluded from the # stream, even if their kinds match. def begin_group kind if @group_excluded @group_excluded += 1 elsif include_group? kind super else @group_excluded = 1 end end # See +begin_group+. def begin_line kind if @group_excluded @group_excluded += 1 elsif include_group? kind super else @group_excluded = 1 end end # Take care of re-enabling the delegation of tokens to the output stream # if an exluded group has ended. def end_group kind if @group_excluded @group_excluded -= 1 @group_excluded = false if @group_excluded.zero? else super end end # See +end_group+. def end_line kind if @group_excluded @group_excluded -= 1 @group_excluded = false if @group_excluded.zero? else super end end end end end coderay-1.1.3/lib/coderay/encoders/xml.rb000066400000000000000000000026031366440446100202760ustar00rootroot00000000000000module CodeRay module Encoders # = XML Encoder # # Uses REXML. Very slow. class XML < Encoder register_for :xml FILE_EXTENSION = 'xml' autoload :REXML, 'rexml/document' DEFAULT_OPTIONS = { :tab_width => 8, :pretty => -1, :transitive => false, } protected def setup options super @doc = REXML::Document.new @doc << REXML::XMLDecl.new @tab_width = options[:tab_width] @root = @node = @doc.add_element('coderay-tokens') end def finish options @doc.write @out, options[:pretty], options[:transitive], true super end public def text_token text, kind if kind == :space token = @node else token = @node.add_element kind.to_s end text.scan(/(\x20+)|(\t+)|(\n)|[^\x20\t\n]+/) do |space, tab, nl| case when space token << REXML::Text.new(space, true) when tab token << REXML::Text.new(tab, true) when nl token << REXML::Text.new(nl, true) else token << REXML::Text.new($&) end end end def begin_group kind @node = @node.add_element kind.to_s end def end_group kind if @node == @root raise 'no token to close!' end @node = @node.parent end end end end coderay-1.1.3/lib/coderay/encoders/yaml.rb000066400000000000000000000013101366440446100204320ustar00rootroot00000000000000autoload :YAML, 'yaml' module CodeRay module Encoders # = YAML Encoder # # Slow. class YAML < Encoder register_for :yaml FILE_EXTENSION = 'yaml' protected def setup options super @data = [] end def finish options output ::YAML.dump(@data) end public def text_token text, kind @data << [text, kind] end def begin_group kind @data << [:begin_group, kind] end def end_group kind @data << [:end_group, kind] end def begin_line kind @data << [:begin_line, kind] end def end_line kind @data << [:end_line, kind] end end end end coderay-1.1.3/lib/coderay/for_redcloth.rb000066400000000000000000000056631366440446100203570ustar00rootroot00000000000000module CodeRay # A little hack to enable CodeRay highlighting in RedCloth. # # Usage: # require 'coderay' # require 'coderay/for_redcloth' # RedCloth.new('@[ruby]puts "Hello, World!"@').to_html # # Make sure you have RedCloth 4.0.3 activated, for example by calling # require 'rubygems' # before RedCloth is loaded and before calling CodeRay.for_redcloth. module ForRedCloth def self.install gem 'RedCloth', '>= 4.0.3' if defined? gem require 'redcloth' unless RedCloth::VERSION.to_s >= '4.0.3' if defined? gem raise 'CodeRay.for_redcloth needs RedCloth version 4.0.3 or later. ' + "You have #{RedCloth::VERSION}. Please gem install RedCloth." else $".delete 'redcloth.rb' # sorry, but it works require 'rubygems' return install # retry end end unless RedCloth::VERSION.to_s >= '4.2.2' warn 'CodeRay.for_redcloth works best with RedCloth version 4.2.2 or later.' end RedCloth::TextileDoc.send :include, ForRedCloth::TextileDoc RedCloth::Formatters::HTML.module_eval do def unescape(html) # :nodoc: replacements = { '&' => '&', '"' => '"', '>' => '>', '<' => '<', } html.gsub(/&(?:amp|quot|[gl]t);/) { |entity| replacements[entity] } end undef code, bc_open, bc_close, escape_pre def code(opts) # :nodoc: opts[:block] = true if !opts[:lang] && RedCloth::VERSION.to_s >= '4.2.0' # simulating pre-4.2 behavior if opts[:text].sub!(/\A\[(\w+)\]/, '') if CodeRay::Scanners[$1].lang == :text opts[:text] = $& + opts[:text] else opts[:lang] = $1 end end end if opts[:lang] && !filter_coderay require 'coderay' @in_bc ||= nil format = @in_bc ? :div : :span opts[:text] = unescape(opts[:text]) unless @in_bc highlighted_code = CodeRay.encode opts[:text], opts[:lang], format highlighted_code.sub!(/\A<(span|div)/) { |m| m + pba(@in_bc || opts) } highlighted_code else "#{opts[:text]}" end end def bc_open(opts) # :nodoc: opts[:block] = true @in_bc = opts opts[:lang] ? '' : "" end def bc_close(opts) # :nodoc: opts = @in_bc @in_bc = nil opts[:lang] ? '' : "\n" end def escape_pre(text) # :nodoc: if @in_bc ||= nil text else html_esc(text, :html_escape_preformatted) end end end end module TextileDoc # :nodoc: attr_accessor :filter_coderay end end end CodeRay::ForRedCloth.installcoderay-1.1.3/lib/coderay/helpers/000077500000000000000000000000001366440446100170105ustar00rootroot00000000000000coderay-1.1.3/lib/coderay/helpers/file_type.rb000066400000000000000000000100521366440446100213130ustar00rootroot00000000000000module CodeRay # = FileType # # A simple filetype recognizer. # # == Usage # # # determine the type of the given # lang = FileType[file_name] # # # return :text if the file type is unknown # lang = FileType.fetch file_name, :text # # # try the shebang line, too # lang = FileType.fetch file_name, :text, true module FileType UnknownFileType = Class.new Exception class << self # Try to determine the file type of the file. # # +filename+ is a relative or absolute path to a file. # # The file itself is only accessed when +read_shebang+ is set to true. # That means you can get filetypes from files that don't exist. def [] filename, read_shebang = false name = File.basename filename ext = File.extname(name).sub(/^\./, '') # from last dot, delete the leading dot ext2 = filename.to_s[/\.(.*)/, 1] # from first dot type = TypeFromExt[ext] || TypeFromExt[ext.downcase] || (TypeFromExt[ext2] if ext2) || (TypeFromExt[ext2.downcase] if ext2) || TypeFromName[name] || TypeFromName[name.downcase] type ||= type_from_shebang(filename) if read_shebang type end # This works like Hash#fetch. # # If the filetype cannot be found, the +default+ value # is returned. def fetch filename, default = nil, read_shebang = false if default && block_given? warn 'Block supersedes default value argument; use either.' end if type = self[filename, read_shebang] type else return yield if block_given? return default if default raise UnknownFileType, 'Could not determine type of %p.' % filename end end protected def type_from_shebang filename return unless File.exist? filename File.open filename, 'r' do |f| if first_line = f.gets if type = first_line[TypeFromShebang] type.to_sym end end end end end TypeFromExt = { 'c' => :c, 'cfc' => :xml, 'cfm' => :xml, 'clj' => :clojure, 'css' => :css, 'diff' => :diff, 'dpr' => :delphi, 'erb' => :erb, 'gemspec' => :ruby, 'go' => :go, 'groovy' => :groovy, 'gvy' => :groovy, 'h' => :c, 'haml' => :haml, 'htm' => :html, 'html' => :html, 'html.erb' => :erb, 'java' => :java, 'js' => :java_script, 'json' => :json, 'lua' => :lua, 'mab' => :ruby, 'pas' => :delphi, 'patch' => :diff, 'phtml' => :php, 'php' => :php, 'php3' => :php, 'php4' => :php, 'php5' => :php, 'prawn' => :ruby, 'py' => :python, 'py3' => :python, 'pyw' => :python, 'rake' => :ruby, 'raydebug' => :raydebug, 'rb' => :ruby, 'rbw' => :ruby, 'rhtml' => :erb, 'rjs' => :ruby, 'rpdf' => :ruby, 'ru' => :ruby, # config.ru 'rxml' => :ruby, 'sass' => :sass, 'sql' => :sql, 'taskpaper' => :taskpaper, 'template' => :json, # AWS CloudFormation template 'tmproj' => :xml, 'xaml' => :xml, 'xhtml' => :html, 'xml' => :xml, 'yaml' => :yaml, 'yml' => :yaml, } for cpp_alias in %w[cc cpp cp cxx c++ C hh hpp h++ cu] TypeFromExt[cpp_alias] = :cpp end TypeFromShebang = /\b(?:ruby|perl|python|sh)\b/ TypeFromName = { 'Capfile' => :ruby, 'Rakefile' => :ruby, 'Rantfile' => :ruby, 'Gemfile' => :ruby, 'Guardfile' => :ruby, 'Vagrantfile' => :ruby, 'Appraisals' => :ruby } end end coderay-1.1.3/lib/coderay/helpers/plugin.rb000066400000000000000000000021541366440446100206350ustar00rootroot00000000000000module CodeRay # = Plugin # # Plugins have to include this module. # # IMPORTANT: Use extend for this module. # # See CodeRay::PluginHost for examples. module Plugin attr_reader :plugin_id # Register this class for the given +id+. # # Example: # class MyPlugin < PluginHost::BaseClass # register_for :my_id # ... # end # # See PluginHost.register. def register_for id @plugin_id = id plugin_host.register self, id end # Returns the title of the plugin, or sets it to the # optional argument +title+. def title title = nil if title @title = title.to_s else @title ||= name[/([^:]+)$/, 1] end end # The PluginHost for this Plugin class. def plugin_host host = nil if host.is_a? PluginHost const_set :PLUGIN_HOST, host end self::PLUGIN_HOST end def aliases plugin_host.plugin_hash.inject [] do |aliases, (key, _)| aliases << key if plugin_host[key] == self aliases end end end end coderay-1.1.3/lib/coderay/helpers/plugin_host.rb000066400000000000000000000125271366440446100216770ustar00rootroot00000000000000module CodeRay # = PluginHost # # A simple subclass/subfolder plugin system. # # Example: # class Generators # extend PluginHost # plugin_path 'app/generators' # end # # class Generator # extend Plugin # PLUGIN_HOST = Generators # end # # class FancyGenerator < Generator # register_for :fancy # end # # Generators[:fancy] #-> FancyGenerator # # or # CodeRay.require_plugin 'Generators/fancy' # # or # Generators::Fancy module PluginHost # Raised if Encoders::[] fails because: # * a file could not be found # * the requested Plugin is not registered PluginNotFound = Class.new LoadError HostNotFound = Class.new LoadError PLUGIN_HOSTS = [] PLUGIN_HOSTS_BY_ID = {} # dummy hash # Loads all plugins using list and load. def load_all for plugin in list load plugin end end # Returns the Plugin for +id+. # # Example: # yaml_plugin = MyPluginHost[:yaml] def [] id, *args, &blk plugin = validate_id(id) begin plugin = plugin_hash.[](plugin, *args, &blk) end while plugin.is_a? String plugin end alias load [] # Tries to +load+ the missing plugin by translating +const+ to the # underscore form (eg. LinesOfCode becomes lines_of_code). def const_missing const id = const.to_s. gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2'). downcase load id end class << self # Adds the module/class to the PLUGIN_HOSTS list. def extended mod PLUGIN_HOSTS << mod end end # The path where the plugins can be found. def plugin_path *args unless args.empty? @plugin_path = File.expand_path File.join(*args) end @plugin_path ||= '' end # Map a plugin_id to another. # # Usage: Put this in a file plugin_path/_map.rb. # # class MyColorHost < PluginHost # map :navy => :dark_blue, # :maroon => :brown, # :luna => :moon # end def map hash for from, to in hash from = validate_id from to = validate_id to plugin_hash[from] = to unless plugin_hash.has_key? from end end # Define the default plugin to use when no plugin is found # for a given id, or return the default plugin. # # See also map. # # class MyColorHost < PluginHost # map :navy => :dark_blue # default :gray # end # # MyColorHost.default # loads and returns the Gray plugin def default id = nil if id id = validate_id id raise "The default plugin can't be named \"default\"." if id == :default plugin_hash[:default] = id else load :default end end # Every plugin must register itself for +id+ by calling register_for, # which calls this method. # # See Plugin#register_for. def register plugin, id plugin_hash[validate_id(id)] = plugin end # A Hash of plugion_id => Plugin pairs. def plugin_hash @plugin_hash ||= (@plugin_hash = make_plugin_hash).tap { load_plugin_map } end # Returns an array of all .rb files in the plugin path. # # The extension .rb is not included. def list Dir[path_to('*')].select do |file| File.basename(file)[/^(?!_)\w+\.rb$/] end.map do |file| File.basename(file, '.rb').to_sym end end # Returns an array of all Plugins. # # Note: This loads all plugins using load_all. def all_plugins load_all plugin_hash.values.grep(Class) end # Loads the map file (see map). # # This is done automatically when plugin_path is called. def load_plugin_map mapfile = path_to '_map' if File.exist? mapfile require mapfile true else false end end protected # Return a plugin hash that automatically loads plugins. def make_plugin_hash Hash.new do |h, plugin_id| id = validate_id(plugin_id) path = path_to id begin require path rescue LoadError => boom if h.has_key?(:default) h[:default] else raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom] end else # Plugin should have registered by now if h.has_key? id h[id] else raise PluginNotFound, "No #{self.name} plugin for #{id.inspect} found in #{path}." end end end end # Returns the expected path to the plugin file for the given id. def path_to plugin_id File.join plugin_path, "#{plugin_id}.rb" end # Converts +id+ to a valid plugin ID String, or returns +nil+. # # Raises +ArgumentError+ for all other objects, or if the # given String includes non-alphanumeric characters (\W). def validate_id id case id when Symbol id.to_s when String if id[/\w+/] == id id.downcase else raise ArgumentError, "Invalid id given: #{id}" end else raise ArgumentError, "Symbol or String expected, but #{id.class} given." end end end end coderay-1.1.3/lib/coderay/helpers/word_list.rb000066400000000000000000000031761366440446100213520ustar00rootroot00000000000000module CodeRay # = WordList # # A Hash subclass designed for mapping word lists to token types. # # A WordList is a Hash with some additional features. # It is intended to be used for keyword recognition. # # WordList is optimized to be used in Scanners, # typically to decide whether a given ident is a special token. # # For case insensitive words use WordList::CaseIgnoring. # # Example: # # # define word arrays # RESERVED_WORDS = %w[ # asm break case continue default do else # ] # # PREDEFINED_TYPES = %w[ # int long short char void # ] # # # make a WordList # IDENT_KIND = WordList.new(:ident). # add(RESERVED_WORDS, :reserved). # add(PREDEFINED_TYPES, :predefined_type) # # ... # # def scan_tokens tokens, options # ... # # elsif scan(/[A-Za-z_][A-Za-z_0-9]*/) # # use it # kind = IDENT_KIND[match] # ... class WordList < Hash # Create a new WordList with +default+ as default value. def initialize default = false super default end # Add words to the list and associate them with +value+. # # Returns +self+, so you can concat add calls. def add words, value = true words.each { |word| self[word] = value } self end end # A CaseIgnoring WordList is like a WordList, only that # keys are compared case-insensitively (normalizing keys using +downcase+). class WordList::CaseIgnoring < WordList def [] key super key.downcase end def []= key, value super key.downcase, value end end end coderay-1.1.3/lib/coderay/scanners.rb000066400000000000000000000012271366440446100175110ustar00rootroot00000000000000require 'strscan' module CodeRay autoload :WordList, coderay_path('helpers', 'word_list') # = Scanners # # This module holds the Scanner class and its subclasses. # For example, the Ruby scanner is named CodeRay::Scanners::Ruby # can be found in coderay/scanners/ruby. # # Scanner also provides methods and constants for the register # mechanism and the [] method that returns the Scanner class # belonging to the given lang. # # See PluginHost. module Scanners extend PluginHost plugin_path File.dirname(__FILE__), 'scanners' autoload :Scanner, CodeRay.coderay_path('scanners', 'scanner') end end coderay-1.1.3/lib/coderay/scanners/000077500000000000000000000000001366440446100171625ustar00rootroot00000000000000coderay-1.1.3/lib/coderay/scanners/_map.rb000066400000000000000000000007661366440446100204340ustar00rootroot00000000000000module CodeRay module Scanners map \ :'c++' => :cpp, :cplusplus => :cpp, :ecmascript => :java_script, :ecma_script => :java_script, :rhtml => :erb, :eruby => :erb, :irb => :ruby, :javascript => :java_script, :js => :java_script, :pascal => :delphi, :patch => :diff, :plain => :text, :plaintext => :text, :xhtml => :html, :yml => :yaml default :text end end coderay-1.1.3/lib/coderay/scanners/c.rb000066400000000000000000000130031366440446100177260ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for C. class C < Scanner register_for :c file_extension 'c' KEYWORDS = [ 'asm', 'break', 'case', 'continue', 'default', 'do', 'else', 'enum', 'for', 'goto', 'if', 'return', 'sizeof', 'struct', 'switch', 'typedef', 'union', 'while', 'restrict', # added in C99 ] # :nodoc: PREDEFINED_TYPES = [ 'int', 'long', 'short', 'char', 'signed', 'unsigned', 'float', 'double', 'bool', 'complex', # added in C99 ] # :nodoc: PREDEFINED_CONSTANTS = [ 'EOF', 'NULL', 'true', 'false', # added in C99 ] # :nodoc: DIRECTIVES = [ 'auto', 'extern', 'register', 'static', 'void', 'const', 'volatile', # added in C89 'inline', # added in C99 ] # :nodoc: IDENT_KIND = WordList.new(:ident). add(KEYWORDS, :keyword). add(PREDEFINED_TYPES, :predefined_type). add(DIRECTIVES, :directive). add(PREDEFINED_CONSTANTS, :predefined_constant) # :nodoc: ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: protected def scan_tokens encoder, options state = :initial label_expected = true case_expected = false label_expected_before_preproc_line = nil in_preproc_line = false until eos? case state when :initial if match = scan(/ \s+ | \\\n /x) if in_preproc_line && match != "\\\n" && match.index(?\n) in_preproc_line = false label_expected = label_expected_before_preproc_line end encoder.text_token match, :space elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) encoder.text_token match, :comment elsif match = scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x) label_expected = match =~ /[;\{\}]/ if case_expected label_expected = true if match == ':' case_expected = false end encoder.text_token match, :operator elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) kind = IDENT_KIND[match] if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/) kind = :label match << matched else label_expected = false if kind == :keyword case match when 'case', 'default' case_expected = true end end end encoder.text_token match, kind elsif match = scan(/L?"/) encoder.begin_group :string if match[0] == ?L encoder.text_token 'L', :modifier match = '"' end encoder.text_token match, :delimiter state = :string elsif match = scan(/ \# \s* if \s* 0 /x) match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos? encoder.text_token match, :comment elsif match = scan(/#[ \t]*(\w*)/) encoder.text_token match, :preprocessor in_preproc_line = true label_expected_before_preproc_line = label_expected state = :include_expected if self[1] == 'include' elsif match = scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox) label_expected = false encoder.text_token match, :char elsif match = scan(/\$/) encoder.text_token match, :ident elsif match = scan(/0[xX][0-9A-Fa-f]+/) label_expected = false encoder.text_token match, :hex elsif match = scan(/(?:0[0-7]+)(?![89.eEfF])/) label_expected = false encoder.text_token match, :octal elsif match = scan(/(?:\d+)(?![.eEfF])L?L?/) label_expected = false encoder.text_token match, :integer elsif match = scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/) label_expected = false encoder.text_token match, :float else encoder.text_token getch, :error end when :string if match = scan(/[^\\\n"]+/) encoder.text_token match, :content elsif match = scan(/"/) encoder.text_token match, :delimiter encoder.end_group :string state = :initial label_expected = false elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) encoder.text_token match, :char elsif match = scan(/ \\ | $ /x) encoder.end_group :string encoder.text_token match, :error unless match.empty? state = :initial label_expected = false else raise_inspect "else case \" reached; %p not handled." % peek(1), encoder end when :include_expected if match = scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/) encoder.text_token match, :include state = :initial elsif match = scan(/\s+/) encoder.text_token match, :space state = :initial if match.index ?\n else state = :initial end else raise_inspect 'Unknown state', encoder end end if state == :string encoder.end_group :string end encoder end end end end coderay-1.1.3/lib/coderay/scanners/clojure.rb000066400000000000000000000244151366440446100211600ustar00rootroot00000000000000# encoding: utf-8 module CodeRay module Scanners # Clojure scanner by Licenser. class Clojure < Scanner register_for :clojure file_extension 'clj' SPECIAL_FORMS = %w[ def if do let quote var fn loop recur throw try catch monitor-enter monitor-exit . new ] # :nodoc: CORE_FORMS = %w[ + - -> ->> .. / * <= < = == >= > accessor aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for bases bean bigdec bigint binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* bound? butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec decimal? declare definline defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay delay? deliver denominator deref derive descendants disj disj! dissoc dissoc! distinct distinct? doall doc dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq error-handler error-mode eval even? every? extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter find find-doc find-ns find-var first float float-array float? floats flush fn fn? fnext for force format future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator hash hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map? mapcat max max-key memfn memoize merge merge-with meta methods min min-key mod name namespace neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext num number? numerator object-array odd? or parents partial partition pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers print print-namespace-doc print-str printf println println-str prn prn-str promise proxy proxy-mappings proxy-super push-thread-bindings pvalues quot rand rand-int range ratio? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string reduce ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-error-handler! set-error-mode! set-validator! set? short short-array shorts shutdown-agents slurp some sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-form-anchor special-symbol? split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync syntax-symbol-anchor take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-dec unchecked-divide unchecked-inc unchecked-multiply unchecked-negate unchecked-remainder unchecked-subtract underive update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-local-vars with-meta with-open with-out-str with-precision xml-seq zero? zipmap ] # :nodoc: PREDEFINED_CONSTANTS = %w[ true false nil *1 *2 *3 *agent* *clojure-version* *command-line-args* *compile-files* *compile-path* *e *err* *file* *flush-on-newline* *in* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *warn-on-reflection* ] # :nodoc: IDENT_KIND = WordList.new(:ident). add(SPECIAL_FORMS, :keyword). add(CORE_FORMS, :keyword). add(PREDEFINED_CONSTANTS, :predefined_constant) KEYWORD_NEXT_TOKEN_KIND = WordList.new(nil). add(%w[ def defn defn- definline defmacro defmulti defmethod defstruct defonce declare ], :function). add(%w[ ns ], :namespace). add(%w[ defprotocol defrecord ], :class) BASIC_IDENTIFIER = /[a-zA-Z$%*\/_+!?&<>\-=]=?[a-zA-Z0-9$&*+!\/_?<>\-\#]*/ IDENTIFIER = /(?!-\d)(?:(?:#{BASIC_IDENTIFIER}\.)*#{BASIC_IDENTIFIER}(?:\/#{BASIC_IDENTIFIER})?\.?)|\.\.?/ SYMBOL = /::?#{IDENTIFIER}/o DIGIT = /\d/ DIGIT10 = DIGIT DIGIT16 = /[0-9a-f]/i DIGIT8 = /[0-7]/ DIGIT2 = /[01]/ RADIX16 = /\#x/i RADIX8 = /\#o/i RADIX2 = /\#b/i RADIX10 = /\#d/i EXACTNESS = /#i|#e/i SIGN = /[\+-]?/ EXP_MARK = /[esfdl]/i EXP = /#{EXP_MARK}#{SIGN}#{DIGIT}+/ SUFFIX = /#{EXP}?/ PREFIX10 = /#{RADIX10}?#{EXACTNESS}?|#{EXACTNESS}?#{RADIX10}?/ PREFIX16 = /#{RADIX16}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX16}/ PREFIX8 = /#{RADIX8}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX8}/ PREFIX2 = /#{RADIX2}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX2}/ UINT10 = /#{DIGIT10}+#*/ UINT16 = /#{DIGIT16}+#*/ UINT8 = /#{DIGIT8}+#*/ UINT2 = /#{DIGIT2}+#*/ DECIMAL = /#{DIGIT10}+#+\.#*#{SUFFIX}|#{DIGIT10}+\.#{DIGIT10}*#*#{SUFFIX}|\.#{DIGIT10}+#*#{SUFFIX}|#{UINT10}#{EXP}/ UREAL10 = /#{UINT10}\/#{UINT10}|#{DECIMAL}|#{UINT10}/ UREAL16 = /#{UINT16}\/#{UINT16}|#{UINT16}/ UREAL8 = /#{UINT8}\/#{UINT8}|#{UINT8}/ UREAL2 = /#{UINT2}\/#{UINT2}|#{UINT2}/ REAL10 = /#{SIGN}#{UREAL10}/ REAL16 = /#{SIGN}#{UREAL16}/ REAL8 = /#{SIGN}#{UREAL8}/ REAL2 = /#{SIGN}#{UREAL2}/ IMAG10 = /i|#{UREAL10}i/ IMAG16 = /i|#{UREAL16}i/ IMAG8 = /i|#{UREAL8}i/ IMAG2 = /i|#{UREAL2}i/ COMPLEX10 = /#{REAL10}@#{REAL10}|#{REAL10}\+#{IMAG10}|#{REAL10}-#{IMAG10}|\+#{IMAG10}|-#{IMAG10}|#{REAL10}/ COMPLEX16 = /#{REAL16}@#{REAL16}|#{REAL16}\+#{IMAG16}|#{REAL16}-#{IMAG16}|\+#{IMAG16}|-#{IMAG16}|#{REAL16}/ COMPLEX8 = /#{REAL8}@#{REAL8}|#{REAL8}\+#{IMAG8}|#{REAL8}-#{IMAG8}|\+#{IMAG8}|-#{IMAG8}|#{REAL8}/ COMPLEX2 = /#{REAL2}@#{REAL2}|#{REAL2}\+#{IMAG2}|#{REAL2}-#{IMAG2}|\+#{IMAG2}|-#{IMAG2}|#{REAL2}/ NUM10 = /#{PREFIX10}?#{COMPLEX10}/ NUM16 = /#{PREFIX16}#{COMPLEX16}/ NUM8 = /#{PREFIX8}#{COMPLEX8}/ NUM2 = /#{PREFIX2}#{COMPLEX2}/ NUM = /#{NUM10}|#{NUM16}|#{NUM8}|#{NUM2}/ protected def scan_tokens encoder, options state = :initial kind = nil until eos? case state when :initial if match = scan(/ \s+ | \\\n | , /x) encoder.text_token match, :space elsif match = scan(/['`\(\[\)\]\{\}]|\#[({]|~@?|[@\^]/) encoder.text_token match, :operator elsif match = scan(/;.*/) encoder.text_token match, :comment # TODO: recognize (comment ...) too elsif match = scan(/\#?\\(?:newline|space|.?)/) encoder.text_token match, :char elsif match = scan(/\#[ft]/) encoder.text_token match, :predefined_constant elsif match = scan(/#{IDENTIFIER}/o) kind = IDENT_KIND[match] encoder.text_token match, kind if rest? && kind == :keyword if kind = KEYWORD_NEXT_TOKEN_KIND[match] encoder.text_token match, :space if match = scan(/\s+/o) encoder.text_token match, kind if match = scan(/#{IDENTIFIER}/o) end end elsif match = scan(/#{SYMBOL}/o) encoder.text_token match, :symbol elsif match = scan(/\./) encoder.text_token match, :operator elsif match = scan(/ \# \^ #{IDENTIFIER} /ox) encoder.text_token match, :type elsif match = scan(/ (\#)? " /x) state = self[1] ? :regexp : :string encoder.begin_group state encoder.text_token match, :delimiter elsif match = scan(/#{NUM}/o) and not matched.empty? encoder.text_token match, match[/[.e\/]/i] ? :float : :integer else encoder.text_token getch, :error end when :string, :regexp if match = scan(/[^"\\]+|\\.?/) encoder.text_token match, :content elsif match = scan(/"/) encoder.text_token match, :delimiter encoder.end_group state state = :initial else raise_inspect "else case \" reached; %p not handled." % peek(1), encoder, state end else raise 'else case reached' end end if [:string, :regexp].include? state encoder.end_group state end encoder end end end endcoderay-1.1.3/lib/coderay/scanners/cpp.rb000066400000000000000000000151151366440446100202740ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for C++. # # Aliases: +cplusplus+, c++ class CPlusPlus < Scanner register_for :cpp file_extension 'cpp' title 'C++' #-- http://www.cppreference.com/wiki/keywords/start KEYWORDS = [ 'and', 'and_eq', 'asm', 'bitand', 'bitor', 'break', 'case', 'catch', 'class', 'compl', 'const_cast', 'continue', 'default', 'delete', 'do', 'dynamic_cast', 'else', 'enum', 'export', 'for', 'goto', 'if', 'namespace', 'new', 'not', 'not_eq', 'or', 'or_eq', 'reinterpret_cast', 'return', 'sizeof', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'throw', 'try', 'typedef', 'typeid', 'typename', 'union', 'while', 'xor', 'xor_eq', ] # :nodoc: PREDEFINED_TYPES = [ 'bool', 'char', 'char16_t', 'char32_t', 'double', 'float', 'int', 'long', 'short', 'signed', 'unsigned', 'wchar_t', 'string', ] # :nodoc: PREDEFINED_CONSTANTS = [ 'false', 'true', 'EOF', 'NULL', 'nullptr' ] # :nodoc: PREDEFINED_VARIABLES = [ 'this', ] # :nodoc: DIRECTIVES = [ 'alignas', 'alignof', 'auto', 'const', 'constexpr', 'decltype', 'explicit', 'extern', 'final', 'friend', 'inline', 'mutable', 'noexcept', 'operator', 'override', 'private', 'protected', 'public', 'register', 'static', 'thread_local', 'using', 'virtual', 'void', 'volatile', ] # :nodoc: IDENT_KIND = WordList.new(:ident). add(KEYWORDS, :keyword). add(PREDEFINED_TYPES, :predefined_type). add(PREDEFINED_VARIABLES, :local_variable). add(DIRECTIVES, :directive). add(PREDEFINED_CONSTANTS, :predefined_constant) # :nodoc: ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: protected def scan_tokens encoder, options state = :initial label_expected = true case_expected = false label_expected_before_preproc_line = nil in_preproc_line = false until eos? case state when :initial if match = scan(/ \s+ | \\\n /x) if in_preproc_line && match != "\\\n" && match.index(?\n) in_preproc_line = false label_expected = label_expected_before_preproc_line end encoder.text_token match, :space elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) encoder.text_token match, :comment elsif match = scan(/ \# \s* if \s* 0 /x) match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos? encoder.text_token match, :comment elsif match = scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x) label_expected = match =~ /[;\{\}]/ if case_expected label_expected = true if match == ':' case_expected = false end encoder.text_token match, :operator elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) kind = IDENT_KIND[match] if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/) kind = :label match << matched else label_expected = false if kind == :keyword case match when 'class' state = :class_name_expected when 'case', 'default' case_expected = true end end end encoder.text_token match, kind elsif match = scan(/\$/) encoder.text_token match, :ident elsif match = scan(/L?"/) encoder.begin_group :string if match[0] == ?L encoder.text_token match, 'L', :modifier match = '"' end state = :string encoder.text_token match, :delimiter elsif match = scan(/#[ \t]*(\w*)/) encoder.text_token match, :preprocessor in_preproc_line = true label_expected_before_preproc_line = label_expected state = :include_expected if self[1] == 'include' elsif match = scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox) label_expected = false encoder.text_token match, :char elsif match = scan(/0[xX][0-9A-Fa-f]+/) label_expected = false encoder.text_token match, :hex elsif match = scan(/(?:0[0-7]+)(?![89.eEfF])/) label_expected = false encoder.text_token match, :octal elsif match = scan(/(?:\d+)(?![.eEfF])L?L?/) label_expected = false encoder.text_token match, :integer elsif match = scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/) label_expected = false encoder.text_token match, :float else encoder.text_token getch, :error end when :string if match = scan(/[^\\"]+/) encoder.text_token match, :content elsif match = scan(/"/) encoder.text_token match, :delimiter encoder.end_group :string state = :initial label_expected = false elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) encoder.text_token match, :char elsif match = scan(/ \\ | $ /x) encoder.end_group :string encoder.text_token match, :error unless match.empty? state = :initial label_expected = false else raise_inspect "else case \" reached; %p not handled." % peek(1), encoder end when :include_expected if match = scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/) encoder.text_token match, :include state = :initial elsif match = scan(/\s+/) encoder.text_token match, :space state = :initial if match.index ?\n else state = :initial end when :class_name_expected if match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) encoder.text_token match, :class state = :initial elsif match = scan(/\s+/) encoder.text_token match, :space else encoder.text_token getch, :error state = :initial end else raise_inspect 'Unknown state', encoder end end if state == :string encoder.end_group :string end encoder end end end end coderay-1.1.3/lib/coderay/scanners/css.rb000066400000000000000000000135751366440446100203120ustar00rootroot00000000000000module CodeRay module Scanners class CSS < Scanner register_for :css KINDS_NOT_LOC = [ :comment, :class, :pseudo_class, :tag, :id, :directive, :key, :value, :operator, :color, :float, :string, :error, :important, :type, ] # :nodoc: module RE # :nodoc: Hex = /[0-9a-fA-F]/ Unicode = /\\#{Hex}{1,6}\b/ # differs from standard because it allows uppercase hex too Escape = /#{Unicode}|\\[^\n0-9a-fA-F]/ NMChar = /[-_a-zA-Z0-9]/ NMStart = /[_a-zA-Z]/ String1 = /"(?:[^\n\\"]+|\\\n|#{Escape})*"?/ # TODO: buggy regexp String2 = /'(?:[^\n\\']+|\\\n|#{Escape})*'?/ # TODO: buggy regexp String = /#{String1}|#{String2}/ HexColor = /#(?:#{Hex}{6}|#{Hex}{3})/ Num = /-?(?:[0-9]*\.[0-9]+|[0-9]+)n?/ Name = /#{NMChar}+/ Ident = /-?#{NMStart}#{NMChar}*/ AtKeyword = /@#{Ident}/ Percentage = /#{Num}%/ reldimensions = %w[em ex px] absdimensions = %w[in cm mm pt pc] Unit = Regexp.union(*(reldimensions + absdimensions + %w[s dpi dppx deg])) Dimension = /#{Num}#{Unit}/ Function = /(?:url|alpha|attr|counters?)\((?:[^)\n]|\\\))*\)?/ Id = /(?!#{HexColor}\b(?!-))##{Name}/ Class = /\.#{Name}/ PseudoClass = /::?#{Ident}/ AttributeSelector = /\[[^\]]*\]?/ end protected def setup @state = :initial @value_expected = false end def scan_tokens encoder, options states = Array(options[:state] || @state).dup value_expected = @value_expected until eos? if match = scan(/\s+/) encoder.text_token match, :space elsif case states.last when :initial, :media if match = scan(/(?>#{RE::Ident})(?!\()|\*/ox) encoder.text_token match, :tag next elsif match = scan(RE::Class) encoder.text_token match, :class next elsif match = scan(RE::Id) encoder.text_token match, :id next elsif match = scan(RE::PseudoClass) encoder.text_token match, :pseudo_class next elsif match = scan(RE::AttributeSelector) # TODO: Improve highlighting inside of attribute selectors. encoder.text_token match[0,1], :operator encoder.text_token match[1..-2], :attribute_name if match.size > 2 encoder.text_token match[-1,1], :operator if match[-1] == ?] next elsif match = scan(/@media/) encoder.text_token match, :directive states.push :media_before_name next end when :block if match = scan(/(?>#{RE::Ident})(?!\()/ox) if value_expected encoder.text_token match, :value else encoder.text_token match, :key end next end when :media_before_name if match = scan(RE::Ident) encoder.text_token match, :type states[-1] = :media_after_name next end when :media_after_name if match = scan(/\{/) encoder.text_token match, :operator states[-1] = :media next end else #:nocov: raise_inspect 'Unknown state', encoder #:nocov: end elsif match = scan(/\/\*(?:.*?\*\/|\z)/m) encoder.text_token match, :comment elsif match = scan(/\{/) value_expected = false encoder.text_token match, :operator states.push :block elsif match = scan(/\}/) value_expected = false encoder.text_token match, :operator if states.last == :block || states.last == :media states.pop end elsif match = scan(/#{RE::String}/o) encoder.begin_group :string encoder.text_token match[0, 1], :delimiter encoder.text_token match[1..-2], :content if match.size > 2 encoder.text_token match[-1, 1], :delimiter if match.size >= 2 encoder.end_group :string elsif match = scan(/#{RE::Function}/o) encoder.begin_group :function start = match[/^\w+\(/] encoder.text_token start, :delimiter if match[-1] == ?) encoder.text_token match[start.size..-2], :content if match.size > start.size + 1 encoder.text_token ')', :delimiter else encoder.text_token match[start.size..-1], :content if match.size > start.size end encoder.end_group :function elsif match = scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox) encoder.text_token match, :float elsif match = scan(/#{RE::HexColor}/o) encoder.text_token match, :color elsif match = scan(/! *important/) encoder.text_token match, :important elsif match = scan(/(?:rgb|hsl)a?\([^()\n]*\)?/) encoder.text_token match, :color elsif match = scan(RE::AtKeyword) encoder.text_token match, :directive elsif match = scan(/ [+>~:;,.=()\/] /x) if match == ':' value_expected = true elsif match == ';' value_expected = false end encoder.text_token match, :operator else encoder.text_token getch, :error end end if options[:keep_state] @state = states @value_expected = value_expected end encoder end end end end coderay-1.1.3/lib/coderay/scanners/debug.rb000066400000000000000000000033721366440446100206020ustar00rootroot00000000000000require 'set' module CodeRay module Scanners # = Debug Scanner # # Interprets the output of the Encoders::Debug encoder (basically the inverse function). class Debug < Scanner register_for :debug title 'CodeRay Token Dump Import' protected def setup super @known_token_kinds = TokenKinds.keys.map(&:to_s).to_set end def scan_tokens encoder, options opened_tokens = [] until eos? if match = scan(/\s+/) encoder.text_token match, :space elsif match = scan(/ (\w+) \( ( [^\)\\]* ( \\. [^\)\\]* )* ) \)? /x) if @known_token_kinds.include? self[1] encoder.text_token self[2].gsub(/\\(.)/m, '\1'), self[1].to_sym else encoder.text_token matched, :unknown end elsif match = scan(/ (\w+) ([<\[]) /x) if @known_token_kinds.include? self[1] kind = self[1].to_sym else kind = :unknown end opened_tokens << kind case self[2] when '<' encoder.begin_group kind when '[' encoder.begin_line kind else raise 'CodeRay bug: This case should not be reached.' end elsif !opened_tokens.empty? && match = scan(/ > /x) encoder.end_group opened_tokens.pop elsif !opened_tokens.empty? && match = scan(/ \] /x) encoder.end_line opened_tokens.pop else encoder.text_token getch, :space end end encoder.end_group opened_tokens.pop until opened_tokens.empty? encoder end end end end coderay-1.1.3/lib/coderay/scanners/delphi.rb000066400000000000000000000112071366440446100207550ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for the Delphi language (Object Pascal). # # Alias: +pascal+ class Delphi < Scanner register_for :delphi file_extension 'pas' KEYWORDS = [ 'and', 'array', 'as', 'at', 'asm', 'at', 'begin', 'case', 'class', 'const', 'constructor', 'destructor', 'dispinterface', 'div', 'do', 'downto', 'else', 'end', 'except', 'exports', 'file', 'finalization', 'finally', 'for', 'function', 'goto', 'if', 'implementation', 'in', 'inherited', 'initialization', 'inline', 'interface', 'is', 'label', 'library', 'mod', 'nil', 'not', 'object', 'of', 'or', 'out', 'packed', 'procedure', 'program', 'property', 'raise', 'record', 'repeat', 'resourcestring', 'set', 'shl', 'shr', 'string', 'then', 'threadvar', 'to', 'try', 'type', 'unit', 'until', 'uses', 'var', 'while', 'with', 'xor', 'on', ] # :nodoc: DIRECTIVES = [ 'absolute', 'abstract', 'assembler', 'at', 'automated', 'cdecl', 'contains', 'deprecated', 'dispid', 'dynamic', 'export', 'external', 'far', 'forward', 'implements', 'local', 'near', 'nodefault', 'on', 'overload', 'override', 'package', 'pascal', 'platform', 'private', 'protected', 'public', 'published', 'read', 'readonly', 'register', 'reintroduce', 'requires', 'resident', 'safecall', 'stdcall', 'stored', 'varargs', 'virtual', 'write', 'writeonly', ] # :nodoc: IDENT_KIND = WordList::CaseIgnoring.new(:ident). add(KEYWORDS, :keyword). add(DIRECTIVES, :directive) # :nodoc: NAME_FOLLOWS = WordList::CaseIgnoring.new(false). add(%w(procedure function .)) # :nodoc: protected def scan_tokens encoder, options state = :initial last_token = '' until eos? if state == :initial if match = scan(/ \s+ /x) encoder.text_token match, :space next elsif match = scan(%r! \{ \$ [^}]* \}? | \(\* \$ (?: .*? \*\) | .* ) !mx) encoder.text_token match, :preprocessor next elsif match = scan(%r! // [^\n]* | \{ [^}]* \}? | \(\* (?: .*? \*\) | .* ) !mx) encoder.text_token match, :comment next elsif match = scan(/ <[>=]? | >=? | :=? | [-+=*\/;,@\^|\(\)\[\]] | \.\. /x) encoder.text_token match, :operator elsif match = scan(/\./) encoder.text_token match, :operator next if last_token == 'end' elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) encoder.text_token match, NAME_FOLLOWS[last_token] ? :ident : IDENT_KIND[match] elsif match = skip(/ ' ( [^\n']|'' ) (?:'|$) /x) encoder.begin_group :char encoder.text_token "'", :delimiter encoder.text_token self[1], :content encoder.text_token "'", :delimiter encoder.end_group :char next elsif match = scan(/ ' /x) encoder.begin_group :string encoder.text_token match, :delimiter state = :string elsif match = scan(/ \# (?: \d+ | \$[0-9A-Fa-f]+ ) /x) encoder.text_token match, :char elsif match = scan(/ \$ [0-9A-Fa-f]+ /x) encoder.text_token match, :hex elsif match = scan(/ (?: \d+ ) (?![eE]|\.[^.]) /x) encoder.text_token match, :integer elsif match = scan(/ \d+ (?: \.\d+ (?: [eE][+-]? \d+ )? | [eE][+-]? \d+ ) /x) encoder.text_token match, :float else encoder.text_token getch, :error next end elsif state == :string if match = scan(/[^\n']+/) encoder.text_token match, :content elsif match = scan(/''/) encoder.text_token match, :char elsif match = scan(/'/) encoder.text_token match, :delimiter encoder.end_group :string state = :initial next elsif match = scan(/\n/) encoder.end_group :string encoder.text_token match, :space state = :initial else raise "else case \' reached; %p not handled." % peek(1), encoder end else raise 'else-case reached', encoder end last_token = match end if state == :string encoder.end_group state end encoder end end end end coderay-1.1.3/lib/coderay/scanners/diff.rb000066400000000000000000000177221366440446100204300ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for output of the diff command. # # Alias: +patch+ class Diff < Scanner register_for :diff title 'diff output' DEFAULT_OPTIONS = { :highlight_code => true, :inline_diff => true, } protected def scan_tokens encoder, options line_kind = nil state = :initial deleted_lines_count = 0 scanners = Hash.new do |h, lang| h[lang] = Scanners[lang].new '', :keep_tokens => true, :keep_state => true end content_scanner = scanners[:plain] content_scanner_entry_state = nil until eos? if match = scan(/\n/) deleted_lines_count = 0 unless line_kind == :delete if line_kind encoder.end_line line_kind line_kind = nil end encoder.text_token match, :space next end case state when :initial if match = scan(/--- |\+\+\+ |=+|_+/) encoder.begin_line line_kind = :head encoder.text_token match, :head if match = scan(/[^\x00\n]+?(?=$|[\t\n]| \(revision)/) encoder.text_token match, :filename if options[:highlight_code] && match != '/dev/null' file_type = CodeRay::FileType.fetch(match, :text) file_type = :text if file_type == :diff content_scanner = scanners[file_type] content_scanner_entry_state = nil end end next unless match = scan(/.+/) encoder.text_token match, :plain elsif match = scan(/Index: |Property changes on: /) encoder.begin_line line_kind = :head encoder.text_token match, :head next unless match = scan(/.+/) encoder.text_token match, :plain elsif match = scan(/Added: /) encoder.begin_line line_kind = :head encoder.text_token match, :head next unless match = scan(/.+/) encoder.text_token match, :plain state = :added elsif match = scan(/\\ .*/) encoder.text_token match, :comment elsif match = scan(/@@(?>[^@\n]+)@@/) content_scanner.state = :initial unless match?(/\n\+/) content_scanner_entry_state = nil if check(/\n|$/) encoder.begin_line line_kind = :change else encoder.begin_group :change end encoder.text_token match[0,2], :change encoder.text_token match[2...-2], :plain encoder.text_token match[-2,2], :change encoder.end_group :change unless line_kind next unless match = scan(/.+/) if options[:highlight_code] content_scanner.tokenize match, :tokens => encoder else encoder.text_token match, :plain end next elsif match = scan(/\+/) encoder.begin_line line_kind = :insert encoder.text_token match, :insert next unless match = scan(/.+/) if options[:highlight_code] content_scanner.tokenize match, :tokens => encoder else encoder.text_token match, :plain end next elsif match = scan(/-/) deleted_lines_count += 1 if options[:inline_diff] && deleted_lines_count == 1 && (changed_lines_count = 1 + check(/.*(?:\n\-.*)*/).count("\n")) && changed_lines_count <= 100_000 && match?(/(?>.*(?:\n\-.*){#{changed_lines_count - 1}}(?:\n\+.*){#{changed_lines_count}})$(?!\n\+)/) deleted_lines = Array.new(changed_lines_count) { |i| skip(/\n\-/) if i > 0; scan(/.*/) } inserted_lines = Array.new(changed_lines_count) { |i| skip(/\n\+/) ; scan(/.*/) } deleted_lines_tokenized = [] inserted_lines_tokenized = [] for deleted_line, inserted_line in deleted_lines.zip(inserted_lines) pre, deleted_part, inserted_part, post = diff deleted_line, inserted_line content_scanner_entry_state = content_scanner.state deleted_lines_tokenized << content_scanner.tokenize([pre, deleted_part, post], :tokens => Tokens.new) content_scanner.state = content_scanner_entry_state || :initial inserted_lines_tokenized << content_scanner.tokenize([pre, inserted_part, post], :tokens => Tokens.new) end for pre, deleted_part, post in deleted_lines_tokenized encoder.begin_line :delete encoder.text_token '-', :delete encoder.tokens pre unless deleted_part.empty? encoder.begin_group :eyecatcher encoder.tokens deleted_part encoder.end_group :eyecatcher end encoder.tokens post encoder.end_line :delete encoder.text_token "\n", :space end for pre, inserted_part, post in inserted_lines_tokenized encoder.begin_line :insert encoder.text_token '+', :insert encoder.tokens pre unless inserted_part.empty? encoder.begin_group :eyecatcher encoder.tokens inserted_part encoder.end_group :eyecatcher end encoder.tokens post changed_lines_count -= 1 if changed_lines_count > 0 encoder.end_line :insert encoder.text_token "\n", :space end end line_kind = :insert elsif match = scan(/.*/) encoder.begin_line line_kind = :delete encoder.text_token '-', :delete if options[:highlight_code] if deleted_lines_count == 1 content_scanner_entry_state = content_scanner.state end content_scanner.tokenize match, :tokens => encoder unless match.empty? if !match?(/\n-/) if match?(/\n\+/) content_scanner.state = content_scanner_entry_state || :initial end content_scanner_entry_state = nil end else encoder.text_token match, :plain end end next elsif match = scan(/ .*/) if options[:highlight_code] content_scanner.tokenize match, :tokens => encoder else encoder.text_token match, :plain end next elsif match = scan(/.+/) encoder.begin_line line_kind = :comment encoder.text_token match, :plain else raise_inspect 'else case rached' end when :added if match = scan(/ \+/) encoder.begin_line line_kind = :insert encoder.text_token match, :insert next unless match = scan(/.+/) encoder.text_token match, :plain else state = :initial next end end end encoder.end_line line_kind if line_kind encoder end private def diff a, b # i will be the index of the leftmost difference from the left. i_max = [a.size, b.size].min i = 0 i += 1 while i < i_max && a[i] == b[i] # j_min will be the index of the leftmost difference from the right. j_min = i - i_max # j will be the index of the rightmost difference from the right which # does not precede the leftmost one from the left. j = -1 j -= 1 while j >= j_min && a[j] == b[j] return a[0...i], a[i..j], b[i..j], (j < -1) ? a[j + 1..-1] : '' end end end end coderay-1.1.3/lib/coderay/scanners/erb.rb000066400000000000000000000034161366440446100202630ustar00rootroot00000000000000module CodeRay module Scanners load :html load :ruby # Scanner for HTML ERB templates. class ERB < Scanner register_for :erb title 'HTML ERB Template' KINDS_NOT_LOC = HTML::KINDS_NOT_LOC ERB_RUBY_BLOCK = / (<%(?!%)[-=\#]?) ((?> [^\-%]* # normal* (?> # special (?: %(?!>) | -(?!%>) ) [^\-%]* # normal* )* )) ((?: -?%> )?) /x # :nodoc: START_OF_ERB = / <%(?!%) /x # :nodoc: protected def setup @ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true end def reset_instance super @html_scanner.reset end def scan_tokens encoder, options until eos? if (match = scan_until(/(?=#{START_OF_ERB})/o) || scan_rest) and not match.empty? @html_scanner.tokenize match, :tokens => encoder elsif match = scan(/#{ERB_RUBY_BLOCK}/o) start_tag = self[1] code = self[2] end_tag = self[3] encoder.begin_group :inline encoder.text_token start_tag, :inline_delimiter if start_tag == '<%#' encoder.text_token code, :comment else @ruby_scanner.tokenize code, :tokens => encoder end unless code.empty? encoder.text_token end_tag, :inline_delimiter unless end_tag.empty? encoder.end_group :inline else raise_inspect 'else-case reached!', encoder end end encoder end end end end coderay-1.1.3/lib/coderay/scanners/go.rb000066400000000000000000000150001366440446100201100ustar00rootroot00000000000000module CodeRay module Scanners class Go < Scanner register_for :go file_extension 'go' # http://golang.org/ref/spec#Keywords KEYWORDS = [ 'break', 'default', 'func', 'interface', 'select', 'case', 'defer', 'go', 'map', 'struct', 'chan', 'else', 'goto', 'package', 'switch', 'const', 'fallthrough', 'if', 'range', 'type', 'continue', 'for', 'import', 'return', 'var', ] # :nodoc: # http://golang.org/ref/spec#Types PREDEFINED_TYPES = [ 'bool', 'uint8', 'uint16', 'uint32', 'uint64', 'int8', 'int16', 'int32', 'int64', 'float32', 'float64', 'complex64', 'complex128', 'byte', 'rune', 'string', 'error', 'uint', 'int', 'uintptr', ] # :nodoc: PREDEFINED_CONSTANTS = [ 'nil', 'iota', 'true', 'false', ] # :nodoc: PREDEFINED_FUNCTIONS = %w[ append cap close complex copy delete imag len make new panic print println real recover ] # :nodoc: IDENT_KIND = WordList.new(:ident). add(KEYWORDS, :keyword). add(PREDEFINED_TYPES, :predefined_type). add(PREDEFINED_CONSTANTS, :predefined_constant). add(PREDEFINED_FUNCTIONS, :predefined) # :nodoc: ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: protected def scan_tokens encoder, options state = :initial label_expected = true case_expected = false label_expected_before_preproc_line = nil in_preproc_line = false until eos? case state when :initial if match = scan(/ \s+ | \\\n /x) if in_preproc_line && match != "\\\n" && match.index(?\n) in_preproc_line = false case_expected = false label_expected = label_expected_before_preproc_line end encoder.text_token match, :space elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) encoder.text_token match, :comment elsif match = scan(/ ?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x) if case_expected label_expected = true if match == ':' case_expected = false end encoder.text_token match, :operator elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) kind = IDENT_KIND[match] if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/) kind = :label label_expected = false match << matched else label_expected = false if kind == :keyword case match when 'case', 'default' case_expected = true end end end encoder.text_token match, kind elsif match = scan(/L?"/) encoder.begin_group :string if match[0] == ?L encoder.text_token 'L', :modifier match = '"' end encoder.text_token match, :delimiter state = :string elsif match = scan(/ ` ([^`]+)? (`)? /x) encoder.begin_group :shell encoder.text_token '`', :delimiter encoder.text_token self[1], :content if self[1] encoder.text_token self[2], :delimiter if self[2] encoder.end_group :shell elsif match = scan(/ \# \s* if \s* 0 /x) match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos? encoder.text_token match, :comment elsif match = scan(/#[ \t]*(\w*)/) encoder.text_token match, :preprocessor in_preproc_line = true label_expected_before_preproc_line = label_expected state = :include_expected if self[1] == 'include' elsif match = scan(/ L?' (?: [^\'\n\\] | \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) )? '? /ox) label_expected = false encoder.text_token match, :char elsif match = scan(/\$/) encoder.text_token match, :ident elsif match = scan(/-?\d*(\.\d*)?([eE][+-]?\d+)?i/) label_expected = false encoder.text_token match, :imaginary elsif match = scan(/-?0[xX][0-9A-Fa-f]+/) label_expected = false encoder.text_token match, :hex elsif match = scan(/-?(?:0[0-7]+)(?![89.eEfF])/) label_expected = false encoder.text_token match, :octal elsif match = scan(/-?(?:\d*\.\d+|\d+\.)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+/) label_expected = false encoder.text_token match, :float elsif match = scan(/-?(?:\d+)(?![.eEfF])L?L?/) label_expected = false encoder.text_token match, :integer else encoder.text_token getch, :error end when :string if match = scan(/[^\\\n"]+/) encoder.text_token match, :content elsif match = scan(/"/) encoder.text_token match, :delimiter encoder.end_group :string state = :initial label_expected = false elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) encoder.text_token match, :char elsif match = scan(/ \\ /x) encoder.text_token match, :error elsif match = scan(/$/) encoder.end_group :string state = :initial label_expected = false else raise_inspect "else case \" reached; %p not handled." % peek(1), encoder end when :include_expected if match = scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/) encoder.text_token match, :include state = :initial elsif match = scan(/\s+/) encoder.text_token match, :space state = :initial if match.index ?\n else state = :initial end else raise_inspect 'Unknown state', encoder end end if state == :string encoder.end_group :string end encoder end end end end coderay-1.1.3/lib/coderay/scanners/groovy.rb000066400000000000000000000225661366440446100210470ustar00rootroot00000000000000module CodeRay module Scanners load :java # Scanner for Groovy. class Groovy < Java register_for :groovy # TODO: check list of keywords GROOVY_KEYWORDS = %w[ as assert def in ] # :nodoc: KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[ case instanceof new return throw typeof while as assert in ] # :nodoc: GROOVY_MAGIC_VARIABLES = %w[ it ] # :nodoc: IDENT_KIND = Java::IDENT_KIND.dup. add(GROOVY_KEYWORDS, :keyword). add(GROOVY_MAGIC_VARIABLES, :local_variable) # :nodoc: ESCAPE = / [bfnrtv$\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # :nodoc: no 4-byte unicode chars? U[a-fA-F0-9]{8} REGEXP_ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | \d | [bBdDsSwW\/] /x # :nodoc: # TODO: interpretation inside ', ", / STRING_CONTENT_PATTERN = { "'" => /(?>\\[^\\'\n]+|[^\\'\n]+)+/, '"' => /[^\\$"\n]+/, "'''" => /(?>[^\\']+|'(?!''))+/, '"""' => /(?>[^\\$"]+|"(?!""))+/, '/' => /[^\\$\/\n]+/, } # :nodoc: protected def setup @state = :initial end def scan_tokens encoder, options state = options[:state] || @state inline_block_stack = [] inline_block_paren_depth = nil string_delimiter = nil import_clause = class_name_follows = last_token = after_def = false value_expected = true until eos? case state when :initial if match = scan(/ \s+ | \\\n /x) encoder.text_token match, :space if match.index ?\n import_clause = after_def = false value_expected = true unless value_expected end next elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) value_expected = true after_def = false encoder.text_token match, :comment elsif bol? && match = scan(/ \#!.* /x) encoder.text_token match, :doctype elsif import_clause && match = scan(/ (?!as) #{IDENT} (?: \. #{IDENT} )* (?: \.\* )? /ox) after_def = value_expected = false encoder.text_token match, :include elsif match = scan(/ #{IDENT} | \[\] /ox) kind = IDENT_KIND[match] value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match] if last_token == '.' kind = :ident elsif class_name_follows kind = :class class_name_follows = false elsif after_def && check(/\s*[({]/) kind = :method after_def = false elsif kind == :ident && last_token != '?' && check(/:/) kind = :key else class_name_follows = true if match == 'class' || (import_clause && match == 'as') import_clause = match == 'import' after_def = true if match == 'def' end encoder.text_token match, kind elsif match = scan(/;/) import_clause = after_def = false value_expected = true encoder.text_token match, :operator elsif match = scan(/\{/) class_name_follows = after_def = false value_expected = true encoder.text_token match, :operator if !inline_block_stack.empty? inline_block_paren_depth += 1 end # TODO: ~'...', ~"..." and ~/.../ style regexps elsif match = scan(/ \.\.] | \+\+ | && | \|\| | \*\*=? | ==?~ | <=?>? | [-+*%^~&|>=!]=? | <<>>?=? /x) value_expected = true value_expected = :regexp if match == '~' after_def = false encoder.text_token match, :operator elsif match = scan(/ [)\]}] /x) value_expected = after_def = false if !inline_block_stack.empty? && match == '}' inline_block_paren_depth -= 1 if inline_block_paren_depth == 0 # closing brace of inline block reached encoder.text_token match, :inline_delimiter encoder.end_group :inline state, string_delimiter, inline_block_paren_depth = inline_block_stack.pop next end end encoder.text_token match, :operator elsif check(/[\d.]/) after_def = value_expected = false if match = scan(/0[xX][0-9A-Fa-f]+/) encoder.text_token match, :hex elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/) encoder.text_token match, :octal elsif match = scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/) encoder.text_token match, :float elsif match = scan(/\d+[lLgG]?/) encoder.text_token match, :integer end elsif match = scan(/'''|"""/) after_def = value_expected = false state = :multiline_string encoder.begin_group :string string_delimiter = match encoder.text_token match, :delimiter # TODO: record.'name' syntax elsif match = scan(/["']/) after_def = value_expected = false state = match == '/' ? :regexp : :string encoder.begin_group state string_delimiter = match encoder.text_token match, :delimiter elsif value_expected && match = scan(/\//) after_def = value_expected = false encoder.begin_group :regexp state = :regexp string_delimiter = '/' encoder.text_token match, :delimiter elsif match = scan(/ @ #{IDENT} /ox) after_def = value_expected = false encoder.text_token match, :annotation elsif match = scan(/\//) after_def = false value_expected = true encoder.text_token match, :operator else encoder.text_token getch, :error end when :string, :regexp, :multiline_string if match = scan(STRING_CONTENT_PATTERN[string_delimiter]) encoder.text_token match, :content elsif match = scan(state == :multiline_string ? /'''|"""/ : /["'\/]/) encoder.text_token match, :delimiter if state == :regexp # TODO: regexp modifiers? s, m, x, i? modifiers = scan(/[ix]+/) encoder.text_token modifiers, :modifier if modifiers && !modifiers.empty? end state = :string if state == :multiline_string encoder.end_group state string_delimiter = nil after_def = value_expected = false state = :initial next elsif (state == :string || state == :multiline_string) && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)) if string_delimiter[0] == ?' && !(match == "\\\\" || match == "\\'") encoder.text_token match, :content else encoder.text_token match, :char end elsif state == :regexp && match = scan(/ \\ (?: #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox) encoder.text_token match, :char elsif match = scan(/ \$ #{IDENT} /mox) encoder.begin_group :inline encoder.text_token '$', :inline_delimiter match = match[1..-1] encoder.text_token match, IDENT_KIND[match] encoder.end_group :inline next elsif match = scan(/ \$ \{ /x) encoder.begin_group :inline encoder.text_token match, :inline_delimiter inline_block_stack << [state, string_delimiter, inline_block_paren_depth] inline_block_paren_depth = 1 state = :initial next elsif match = scan(/ \$ /mx) encoder.text_token match, :content elsif match = scan(/ \\. /mx) encoder.text_token match, :content # TODO: Shouldn't this be :error? elsif match = scan(/ \\ | \n /x) encoder.end_group state == :regexp ? :regexp : :string encoder.text_token match, :error after_def = value_expected = false state = :initial else raise_inspect "else case \" reached; %p not handled." % peek(1), encoder end else raise_inspect 'Unknown state', encoder end last_token = match unless [:space, :comment, :doctype].include? kind end if [:multiline_string, :string, :regexp].include? state encoder.end_group state == :regexp ? :regexp : :string end if options[:keep_state] @state = state end until inline_block_stack.empty? state, = *inline_block_stack.pop encoder.end_group :inline encoder.end_group state == :regexp ? :regexp : :string end encoder end end end end coderay-1.1.3/lib/coderay/scanners/haml.rb000066400000000000000000000120741366440446100204340ustar00rootroot00000000000000module CodeRay module Scanners load :ruby load :html load :java_script class HAML < Scanner register_for :haml title 'HAML Template' KINDS_NOT_LOC = HTML::KINDS_NOT_LOC protected def setup super @ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true @embedded_ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true, :state => @ruby_scanner.interpreted_string_state @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true end def scan_tokens encoder, options match = nil code = '' until eos? if bol? if match = scan(/!!!.*/) encoder.text_token match, :doctype next end if match = scan(/(?>( *)(\/(?!\[if)|-\#|:javascript|:ruby|:\w+) *)(?=\n)/) encoder.text_token match, :comment code = self[2] if match = scan(/(?:\n+#{self[1]} .*)+/) case code when '/', '-#' encoder.text_token match, :comment when ':javascript' # TODO: recognize #{...} snippets inside JavaScript @java_script_scanner ||= CodeRay.scanner :java_script, :tokens => @tokens, :keep_tokens => true @java_script_scanner.tokenize match, :tokens => encoder when ':ruby' @ruby_scanner.tokenize match, :tokens => encoder when /:\w+/ encoder.text_token match, :comment else raise 'else-case reached: %p' % [code] end end end if match = scan(/ +/) encoder.text_token match, :space end if match = scan(/\/.*/) encoder.text_token match, :comment next end if match = scan(/\\/) encoder.text_token match, :plain if match = scan(/.+/) @html_scanner.tokenize match, :tokens => encoder end next end tag = false if match = scan(/%[-\w:]+\/?/) encoder.text_token match, :tag # if match = scan(/( +)(.+)/) # encoder.text_token self[1], :space # @embedded_ruby_scanner.tokenize self[2], :tokens => encoder # end tag = true end while match = scan(/([.#])[-\w]*\w/) encoder.text_token match, self[1] == '#' ? :constant : :class tag = true end if tag && match = scan(/(\()([^)]+)?(\))?/) # TODO: recognize title=@title, class="widget_#{@widget.number}" encoder.text_token self[1], :plain @html_scanner.tokenize self[2], :tokens => encoder, :state => :attribute if self[2] encoder.text_token self[3], :plain if self[3] end if tag && match = scan(/\{/) encoder.text_token match, :plain code = '' level = 1 while true code << scan(/([^\{\},\n]|, *\n?)*/) case match = getch when '{' level += 1 code << match when '}' level -= 1 if level > 0 code << match else break end when "\n", ",", nil break end end @ruby_scanner.tokenize code, :tokens => encoder unless code.empty? encoder.text_token match, :plain if match end if tag && match = scan(/(\[)([^\]\n]+)?(\])?/) encoder.text_token self[1], :plain @ruby_scanner.tokenize self[2], :tokens => encoder if self[2] encoder.text_token self[3], :plain if self[3] end if tag && match = scan(/\//) encoder.text_token match, :tag end if scan(/(>? encoder else @ruby_scanner.tokenize self[4], :tokens => encoder end end elsif match = scan(/((?:<|> encoder if self[2] end elsif match = scan(/.+/) @html_scanner.tokenize match, :tokens => encoder end if match = scan(/\n/) encoder.text_token match, :space end end encoder end end end end coderay-1.1.3/lib/coderay/scanners/html.rb000066400000000000000000000212771366440446100204640ustar00rootroot00000000000000module CodeRay module Scanners # HTML Scanner # # Alias: +xhtml+ # # See also: Scanners::XML class HTML < Scanner register_for :html KINDS_NOT_LOC = [ :comment, :doctype, :preprocessor, :tag, :attribute_name, :operator, :attribute_value, :string, :plain, :entity, :error, ] # :nodoc: EVENT_ATTRIBUTES = %w( onabort onafterprint onbeforeprint onbeforeunload onblur oncanplay oncanplaythrough onchange onclick oncontextmenu oncuechange ondblclick ondrag ondragdrop ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onerror onfocus onformchange onforminput onhashchange oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmessage onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onmove onoffline ononline onpagehide onpageshow onpause onplay onplaying onpopstate onprogress onratechange onreadystatechange onredo onreset onresize onscroll onseeked onseeking onselect onshow onstalled onstorage onsubmit onsuspend ontimeupdate onundo onunload onvolumechange onwaiting ) IN_ATTRIBUTE = WordList::CaseIgnoring.new(nil). add(EVENT_ATTRIBUTES, :script). add(['style'], :style) ATTR_NAME = /[\w.:-]+/ # :nodoc: TAG_END = /\/?>/ # :nodoc: HEX = /[0-9a-fA-F]/ # :nodoc: ENTITY = / & (?: \w+ | \# (?: \d+ | x#{HEX}+ ) ) ; /ox # :nodoc: PLAIN_STRING_CONTENT = { "'" => /[^&'>\n]+/, '"' => /[^&">\n]+/, } # :nodoc: def reset super @state = :initial @plain_string_content = nil end protected def setup @state = :initial @plain_string_content = nil @in_tag = nil end def scan_java_script encoder, code if code && !code.empty? @java_script_scanner ||= Scanners::JavaScript.new '', :keep_tokens => true @java_script_scanner.tokenize code, :tokens => encoder end end def scan_css encoder, code, state = [:initial] if code && !code.empty? @css_scanner ||= Scanners::CSS.new '', :keep_tokens => true @css_scanner.tokenize code, :tokens => encoder, :state => state end end def scan_tokens encoder, options state = options[:state] || @state plain_string_content = @plain_string_content in_tag = @in_tag in_attribute = nil encoder.begin_group :string if state == :attribute_value_string until eos? if state != :in_special_tag && match = scan(/\s+/m) encoder.text_token match, :space else case state when :initial if match = scan(//m) encoder.text_token match[0..-4], :plain encoder.text_token ']]>', :inline_delimiter elsif match = scan(/.+/) encoder.text_token match, :error end elsif match = scan(/|.*)/m) encoder.text_token match, :comment elsif match = scan(/|.*)|\]>/m) encoder.text_token match, :doctype elsif match = scan(/<\?xml(?:.*?\?>|.*)/m) encoder.text_token match, :preprocessor elsif match = scan(/<\?(?:.*?\?>|.*)/m) encoder.text_token match, :comment elsif match = scan(/<\/[-\w.:]*>?/m) in_tag = nil encoder.text_token match, :tag elsif match = scan(/<(?:(script|style)|[-\w.:]+)(>)?/m) encoder.text_token match, :tag in_tag = self[1] if self[2] state = :in_special_tag if in_tag else state = :attribute end elsif match = scan(/[^<>&]+/) encoder.text_token match, :plain elsif match = scan(/#{ENTITY}/ox) encoder.text_token match, :entity elsif match = scan(/[<>&]/) in_tag = nil encoder.text_token match, :error else raise_inspect '[BUG] else-case reached with state %p' % [state], encoder end when :attribute if match = scan(/#{TAG_END}/o) encoder.text_token match, :tag in_attribute = nil if in_tag state = :in_special_tag else state = :initial end elsif match = scan(/#{ATTR_NAME}/o) in_attribute = IN_ATTRIBUTE[match] encoder.text_token match, :attribute_name state = :attribute_equal else in_tag = nil encoder.text_token getch, :error end when :attribute_equal if match = scan(/=/) #/ encoder.text_token match, :operator state = :attribute_value else state = :attribute next end when :attribute_value if match = scan(/#{ATTR_NAME}/o) encoder.text_token match, :attribute_value state = :attribute elsif match = scan(/["']/) if in_attribute == :script || in_attribute == :style encoder.begin_group :string encoder.text_token match, :delimiter if scan(/javascript:[ \t]*/) encoder.text_token matched, :comment end code = scan_until(match == '"' ? /(?="|\z)/ : /(?='|\z)/) if in_attribute == :script scan_java_script encoder, code else scan_css encoder, code, [:block] end match = scan(/["']/) encoder.text_token match, :delimiter if match encoder.end_group :string state = :attribute in_attribute = nil else encoder.begin_group :string state = :attribute_value_string plain_string_content = PLAIN_STRING_CONTENT[match] encoder.text_token match, :delimiter end elsif match = scan(/#{TAG_END}/o) encoder.text_token match, :tag state = :initial else encoder.text_token getch, :error end when :attribute_value_string if match = scan(plain_string_content) encoder.text_token match, :content elsif match = scan(/['"]/) encoder.text_token match, :delimiter encoder.end_group :string state = :attribute elsif match = scan(/#{ENTITY}/ox) encoder.text_token match, :entity elsif match = scan(/&/) encoder.text_token match, :content elsif match = scan(/[\n>]/) encoder.end_group :string state = :initial encoder.text_token match, :error end when :in_special_tag case in_tag when 'script', 'style' encoder.text_token match, :space if match = scan(/[ \t]*\n/) if scan(/(\s*)|(.*))/m) code = self[2] || self[4] closing = self[3] encoder.text_token self[1], :comment else code = scan_until(/(?=(?:\n\s*)?<\/#{in_tag}>)|\z/) closing = false end unless code.empty? encoder.begin_group :inline if in_tag == 'script' scan_java_script encoder, code else scan_css encoder, code end encoder.end_group :inline end encoder.text_token closing, :comment if closing state = :initial else raise 'unknown special tag: %p' % [in_tag] end else raise_inspect 'Unknown state: %p' % [state], encoder end end end if options[:keep_state] @state = state @plain_string_content = plain_string_content @in_tag = in_tag end encoder.end_group :string if state == :attribute_value_string encoder end end end end coderay-1.1.3/lib/coderay/scanners/java.rb000066400000000000000000000130531366440446100204320ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for Java. class Java < Scanner register_for :java autoload :BuiltinTypes, CodeRay.coderay_path('scanners', 'java', 'builtin_types') # http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html KEYWORDS = %w[ assert break case catch continue default do else finally for if instanceof import new package return switch throw try typeof while debugger export ] # :nodoc: RESERVED = %w[ const goto ] # :nodoc: CONSTANTS = %w[ false null true ] # :nodoc: MAGIC_VARIABLES = %w[ this super ] # :nodoc: TYPES = %w[ boolean byte char class double enum float int interface long short void var ] << '[]' # :nodoc: because int[] should be highlighted as a type DIRECTIVES = %w[ abstract extends final implements native private protected public static strictfp synchronized throws transient volatile ] # :nodoc: IDENT_KIND = WordList.new(:ident). add(KEYWORDS, :keyword). add(RESERVED, :reserved). add(CONSTANTS, :predefined_constant). add(MAGIC_VARIABLES, :local_variable). add(TYPES, :type). add(BuiltinTypes::List, :predefined_type). add(BuiltinTypes::List.select { |builtin| builtin[/(Error|Exception)$/] }, :exception). add(DIRECTIVES, :directive) # :nodoc: ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: STRING_CONTENT_PATTERN = { "'" => /[^\\']+/, '"' => /[^\\"]+/, '/' => /[^\\\/]+/, } # :nodoc: IDENT = RUBY_VERSION < '1.9' ? /[a-zA-Z_][A-Za-z_0-9]*/ : Regexp.new('[[[:alpha:]]_][[[:alnum:]]_]*') # :nodoc: protected def scan_tokens encoder, options state = :initial string_delimiter = nil package_name_expected = false class_name_follows = false last_token_dot = false until eos? case state when :initial if match = scan(/ \s+ | \\\n /x) encoder.text_token match, :space next elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) encoder.text_token match, :comment next elsif package_name_expected && match = scan(/ #{IDENT} (?: \. #{IDENT} )* /ox) encoder.text_token match, package_name_expected elsif match = scan(/ #{IDENT} | \[\] /ox) kind = IDENT_KIND[match] if last_token_dot kind = :ident elsif class_name_follows kind = :class class_name_follows = false else case match when 'import' package_name_expected = :include when 'package' package_name_expected = :namespace when 'class', 'interface' class_name_follows = true end end encoder.text_token match, kind elsif match = scan(/ \.(?!\d) | [,?:()\[\]}] | -- | \+\+ | && | \|\| | \*\*=? | [-+*\/%^~&|<>=!]=? | <<>>?=? /x) encoder.text_token match, :operator elsif match = scan(/;/) package_name_expected = false encoder.text_token match, :operator elsif match = scan(/\{/) class_name_follows = false encoder.text_token match, :operator elsif check(/[\d.]/) if match = scan(/0[xX][0-9A-Fa-f]+/) encoder.text_token match, :hex elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/) encoder.text_token match, :octal elsif match = scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/) encoder.text_token match, :float elsif match = scan(/\d+[lL]?/) encoder.text_token match, :integer end elsif match = scan(/["']/) state = :string encoder.begin_group state string_delimiter = match encoder.text_token match, :delimiter elsif match = scan(/ @ #{IDENT} /ox) encoder.text_token match, :annotation else encoder.text_token getch, :error end when :string if match = scan(STRING_CONTENT_PATTERN[string_delimiter]) encoder.text_token match, :content elsif match = scan(/["'\/]/) encoder.text_token match, :delimiter encoder.end_group state state = :initial string_delimiter = nil elsif state == :string && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)) if string_delimiter == "'" && !(match == "\\\\" || match == "\\'") encoder.text_token match, :content else encoder.text_token match, :char end elsif match = scan(/\\./m) encoder.text_token match, :content elsif match = scan(/ \\ | $ /x) encoder.end_group state state = :initial encoder.text_token match, :error unless match.empty? else raise_inspect "else case \" reached; %p not handled." % peek(1), encoder end else raise_inspect 'Unknown state', encoder end last_token_dot = match == '.' end if state == :string encoder.end_group state end encoder end end end end coderay-1.1.3/lib/coderay/scanners/java/000077500000000000000000000000001366440446100201035ustar00rootroot00000000000000coderay-1.1.3/lib/coderay/scanners/java/builtin_types.rb000066400000000000000000001215021366440446100233230ustar00rootroot00000000000000module CodeRay module Scanners module Java::BuiltinTypes # :nodoc: #:nocov: List = %w[ AbstractAction AbstractBorder AbstractButton AbstractCellEditor AbstractCollection AbstractColorChooserPanel AbstractDocument AbstractExecutorService AbstractInterruptibleChannel AbstractLayoutCache AbstractList AbstractListModel AbstractMap AbstractMethodError AbstractPreferences AbstractQueue AbstractQueuedSynchronizer AbstractSelectableChannel AbstractSelectionKey AbstractSelector AbstractSequentialList AbstractSet AbstractSpinnerModel AbstractTableModel AbstractUndoableEdit AbstractWriter AccessControlContext AccessControlException AccessController AccessException Accessible AccessibleAction AccessibleAttributeSequence AccessibleBundle AccessibleComponent AccessibleContext AccessibleEditableText AccessibleExtendedComponent AccessibleExtendedTable AccessibleExtendedText AccessibleHyperlink AccessibleHypertext AccessibleIcon AccessibleKeyBinding AccessibleObject AccessibleRelation AccessibleRelationSet AccessibleResourceBundle AccessibleRole AccessibleSelection AccessibleState AccessibleStateSet AccessibleStreamable AccessibleTable AccessibleTableModelChange AccessibleText AccessibleTextSequence AccessibleValue AccountException AccountExpiredException AccountLockedException AccountNotFoundException Acl AclEntry AclNotFoundException Action ActionEvent ActionListener ActionMap ActionMapUIResource Activatable ActivateFailedException ActivationDesc ActivationException ActivationGroup ActivationGroupDesc ActivationGroupID ActivationGroup_Stub ActivationID ActivationInstantiator ActivationMonitor ActivationSystem Activator ActiveEvent ActivityCompletedException ActivityRequiredException Adjustable AdjustmentEvent AdjustmentListener Adler32 AffineTransform AffineTransformOp AlgorithmParameterGenerator AlgorithmParameterGeneratorSpi AlgorithmParameters AlgorithmParameterSpec AlgorithmParametersSpi AllPermission AlphaComposite AlreadyBoundException AlreadyConnectedException AncestorEvent AncestorListener AnnotatedElement Annotation AnnotationFormatError AnnotationTypeMismatchException AppConfigurationEntry Appendable Applet AppletContext AppletInitializer AppletStub Arc2D Area AreaAveragingScaleFilter ArithmeticException Array ArrayBlockingQueue ArrayIndexOutOfBoundsException ArrayList Arrays ArrayStoreException ArrayType AssertionError AsyncBoxView AsynchronousCloseException AtomicBoolean AtomicInteger AtomicIntegerArray AtomicIntegerFieldUpdater AtomicLong AtomicLongArray AtomicLongFieldUpdater AtomicMarkableReference AtomicReference AtomicReferenceArray AtomicReferenceFieldUpdater AtomicStampedReference Attribute AttributeChangeNotification AttributeChangeNotificationFilter AttributedCharacterIterator AttributedString AttributeException AttributeInUseException AttributeList AttributeModificationException AttributeNotFoundException Attributes AttributeSet AttributeSetUtilities AttributeValueExp AudioClip AudioFileFormat AudioFileReader AudioFileWriter AudioFormat AudioInputStream AudioPermission AudioSystem AuthenticationException AuthenticationNotSupportedException Authenticator AuthorizeCallback AuthPermission AuthProvider Autoscroll AWTError AWTEvent AWTEventListener AWTEventListenerProxy AWTEventMulticaster AWTException AWTKeyStroke AWTPermission BackingStoreException BadAttributeValueExpException BadBinaryOpValueExpException BadLocationException BadPaddingException BadStringOperationException BandCombineOp BandedSampleModel BaseRowSet BasicArrowButton BasicAttribute BasicAttributes BasicBorders BasicButtonListener BasicButtonUI BasicCheckBoxMenuItemUI BasicCheckBoxUI BasicColorChooserUI BasicComboBoxEditor BasicComboBoxRenderer BasicComboBoxUI BasicComboPopup BasicControl BasicDesktopIconUI BasicDesktopPaneUI BasicDirectoryModel BasicEditorPaneUI BasicFileChooserUI BasicFormattedTextFieldUI BasicGraphicsUtils BasicHTML BasicIconFactory BasicInternalFrameTitlePane BasicInternalFrameUI BasicLabelUI BasicListUI BasicLookAndFeel BasicMenuBarUI BasicMenuItemUI BasicMenuUI BasicOptionPaneUI BasicPanelUI BasicPasswordFieldUI BasicPermission BasicPopupMenuSeparatorUI BasicPopupMenuUI BasicProgressBarUI BasicRadioButtonMenuItemUI BasicRadioButtonUI BasicRootPaneUI BasicScrollBarUI BasicScrollPaneUI BasicSeparatorUI BasicSliderUI BasicSpinnerUI BasicSplitPaneDivider BasicSplitPaneUI BasicStroke BasicTabbedPaneUI BasicTableHeaderUI BasicTableUI BasicTextAreaUI BasicTextFieldUI BasicTextPaneUI BasicTextUI BasicToggleButtonUI BasicToolBarSeparatorUI BasicToolBarUI BasicToolTipUI BasicTreeUI BasicViewportUI BatchUpdateException BeanContext BeanContextChild BeanContextChildComponentProxy BeanContextChildSupport BeanContextContainerProxy BeanContextEvent BeanContextMembershipEvent BeanContextMembershipListener BeanContextProxy BeanContextServiceAvailableEvent BeanContextServiceProvider BeanContextServiceProviderBeanInfo BeanContextServiceRevokedEvent BeanContextServiceRevokedListener BeanContextServices BeanContextServicesListener BeanContextServicesSupport BeanContextSupport BeanDescriptor BeanInfo Beans BevelBorder Bidi BigDecimal BigInteger BinaryRefAddr BindException Binding BitSet Blob BlockingQueue BlockView BMPImageWriteParam Book Boolean BooleanControl Border BorderFactory BorderLayout BorderUIResource BoundedRangeModel Box BoxLayout BoxView BreakIterator BrokenBarrierException Buffer BufferCapabilities BufferedImage BufferedImageFilter BufferedImageOp BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter BufferOverflowException BufferStrategy BufferUnderflowException Button ButtonGroup ButtonModel ButtonUI Byte ByteArrayInputStream ByteArrayOutputStream ByteBuffer ByteChannel ByteLookupTable ByteOrder CachedRowSet CacheRequest CacheResponse Calendar Callable CallableStatement Callback CallbackHandler CancelablePrintJob CancellationException CancelledKeyException CannotProceedException CannotRedoException CannotUndoException Canvas CardLayout Caret CaretEvent CaretListener CellEditor CellEditorListener CellRendererPane Certificate CertificateEncodingException CertificateException CertificateExpiredException CertificateFactory CertificateFactorySpi CertificateNotYetValidException CertificateParsingException CertPath CertPathBuilder CertPathBuilderException CertPathBuilderResult CertPathBuilderSpi CertPathParameters CertPathTrustManagerParameters CertPathValidator CertPathValidatorException CertPathValidatorResult CertPathValidatorSpi CertSelector CertStore CertStoreException CertStoreParameters CertStoreSpi ChangedCharSetException ChangeEvent ChangeListener Channel Channels Character CharacterCodingException CharacterIterator CharArrayReader CharArrayWriter CharBuffer CharConversionException CharSequence Charset CharsetDecoder CharsetEncoder CharsetProvider Checkbox CheckboxGroup CheckboxMenuItem CheckedInputStream CheckedOutputStream Checksum Choice ChoiceCallback ChoiceFormat Chromaticity Cipher CipherInputStream CipherOutputStream CipherSpi Class ClassCastException ClassCircularityError ClassDefinition ClassDesc ClassFileTransformer ClassFormatError ClassLoader ClassLoaderRepository ClassLoadingMXBean ClassNotFoundException Clip Clipboard ClipboardOwner Clob Cloneable CloneNotSupportedException Closeable ClosedByInterruptException ClosedChannelException ClosedSelectorException CMMException CoderMalfunctionError CoderResult CodeSigner CodeSource CodingErrorAction CollationElementIterator CollationKey Collator Collection CollectionCertStoreParameters Collections Color ColorChooserComponentFactory ColorChooserUI ColorConvertOp ColorModel ColorSelectionModel ColorSpace ColorSupported ColorType ColorUIResource ComboBoxEditor ComboBoxModel ComboBoxUI ComboPopup CommunicationException Comparable Comparator CompilationMXBean Compiler CompletionService Component ComponentAdapter ComponentColorModel ComponentEvent ComponentInputMap ComponentInputMapUIResource ComponentListener ComponentOrientation ComponentSampleModel ComponentUI ComponentView Composite CompositeContext CompositeData CompositeDataSupport CompositeName CompositeType CompositeView CompoundBorder CompoundControl CompoundEdit CompoundName Compression ConcurrentHashMap ConcurrentLinkedQueue ConcurrentMap ConcurrentModificationException Condition Configuration ConfigurationException ConfirmationCallback ConnectException ConnectIOException Connection ConnectionEvent ConnectionEventListener ConnectionPendingException ConnectionPoolDataSource ConsoleHandler Constructor Container ContainerAdapter ContainerEvent ContainerListener ContainerOrderFocusTraversalPolicy ContentHandler ContentHandlerFactory ContentModel Context ContextNotEmptyException ContextualRenderedImageFactory Control ControlFactory ControllerEventListener ConvolveOp CookieHandler Copies CopiesSupported CopyOnWriteArrayList CopyOnWriteArraySet CountDownLatch CounterMonitor CounterMonitorMBean CRC32 CredentialException CredentialExpiredException CredentialNotFoundException CRL CRLException CRLSelector CropImageFilter CSS CubicCurve2D Currency Cursor Customizer CyclicBarrier DatabaseMetaData DataBuffer DataBufferByte DataBufferDouble DataBufferFloat DataBufferInt DataBufferShort DataBufferUShort DataFlavor DataFormatException DatagramChannel DatagramPacket DatagramSocket DatagramSocketImpl DatagramSocketImplFactory DataInput DataInputStream DataLine DataOutput DataOutputStream DataSource DataTruncation DatatypeConfigurationException DatatypeConstants DatatypeFactory Date DateFormat DateFormatSymbols DateFormatter DateTimeAtCompleted DateTimeAtCreation DateTimeAtProcessing DateTimeSyntax DebugGraphics DecimalFormat DecimalFormatSymbols DefaultBoundedRangeModel DefaultButtonModel DefaultCaret DefaultCellEditor DefaultColorSelectionModel DefaultComboBoxModel DefaultDesktopManager DefaultEditorKit DefaultFocusManager DefaultFocusTraversalPolicy DefaultFormatter DefaultFormatterFactory DefaultHighlighter DefaultKeyboardFocusManager DefaultListCellRenderer DefaultListModel DefaultListSelectionModel DefaultLoaderRepository DefaultMenuLayout DefaultMetalTheme DefaultMutableTreeNode DefaultPersistenceDelegate DefaultSingleSelectionModel DefaultStyledDocument DefaultTableCellRenderer DefaultTableColumnModel DefaultTableModel DefaultTextUI DefaultTreeCellEditor DefaultTreeCellRenderer DefaultTreeModel DefaultTreeSelectionModel Deflater DeflaterOutputStream Delayed DelayQueue DelegationPermission Deprecated Descriptor DescriptorAccess DescriptorSupport DESedeKeySpec DesignMode DESKeySpec DesktopIconUI DesktopManager DesktopPaneUI Destination Destroyable DestroyFailedException DGC DHGenParameterSpec DHKey DHParameterSpec DHPrivateKey DHPrivateKeySpec DHPublicKey DHPublicKeySpec Dialog Dictionary DigestException DigestInputStream DigestOutputStream Dimension Dimension2D DimensionUIResource DirContext DirectColorModel DirectoryManager DirObjectFactory DirStateFactory DisplayMode DnDConstants Doc DocAttribute DocAttributeSet DocFlavor DocPrintJob Document DocumentBuilder DocumentBuilderFactory Documented DocumentEvent DocumentFilter DocumentListener DocumentName DocumentParser DomainCombiner DOMLocator DOMResult DOMSource Double DoubleBuffer DragGestureEvent DragGestureListener DragGestureRecognizer DragSource DragSourceAdapter DragSourceContext DragSourceDragEvent DragSourceDropEvent DragSourceEvent DragSourceListener DragSourceMotionListener Driver DriverManager DriverPropertyInfo DropTarget DropTargetAdapter DropTargetContext DropTargetDragEvent DropTargetDropEvent DropTargetEvent DropTargetListener DSAKey DSAKeyPairGenerator DSAParameterSpec DSAParams DSAPrivateKey DSAPrivateKeySpec DSAPublicKey DSAPublicKeySpec DTD DTDConstants DuplicateFormatFlagsException Duration DynamicMBean ECField ECFieldF2m ECFieldFp ECGenParameterSpec ECKey ECParameterSpec ECPoint ECPrivateKey ECPrivateKeySpec ECPublicKey ECPublicKeySpec EditorKit Element ElementIterator ElementType Ellipse2D EllipticCurve EmptyBorder EmptyStackException EncodedKeySpec Encoder EncryptedPrivateKeyInfo Entity Enum EnumConstantNotPresentException EnumControl Enumeration EnumMap EnumSet EnumSyntax EOFException Error ErrorListener ErrorManager EtchedBorder Event EventContext EventDirContext EventHandler EventListener EventListenerList EventListenerProxy EventObject EventQueue EventSetDescriptor Exception ExceptionInInitializerError ExceptionListener Exchanger ExecutionException Executor ExecutorCompletionService Executors ExecutorService ExemptionMechanism ExemptionMechanismException ExemptionMechanismSpi ExpandVetoException ExportException Expression ExtendedRequest ExtendedResponse Externalizable FactoryConfigurationError FailedLoginException FeatureDescriptor Fidelity Field FieldPosition FieldView File FileCacheImageInputStream FileCacheImageOutputStream FileChannel FileChooserUI FileDescriptor FileDialog FileFilter FileHandler FileImageInputStream FileImageOutputStream FileInputStream FileLock FileLockInterruptionException FilenameFilter FileNameMap FileNotFoundException FileOutputStream FilePermission FileReader FileSystemView FileView FileWriter Filter FilteredImageSource FilteredRowSet FilterInputStream FilterOutputStream FilterReader FilterWriter Finishings FixedHeightLayoutCache FlatteningPathIterator FlavorEvent FlavorException FlavorListener FlavorMap FlavorTable Float FloatBuffer FloatControl FlowLayout FlowView Flushable FocusAdapter FocusEvent FocusListener FocusManager FocusTraversalPolicy Font FontFormatException FontMetrics FontRenderContext FontUIResource Format FormatConversionProvider FormatFlagsConversionMismatchException Formattable FormattableFlags Formatter FormatterClosedException FormSubmitEvent FormView Frame Future FutureTask GapContent GarbageCollectorMXBean GatheringByteChannel GaugeMonitor GaugeMonitorMBean GeneralPath GeneralSecurityException GenericArrayType GenericDeclaration GenericSignatureFormatError GlyphJustificationInfo GlyphMetrics GlyphVector GlyphView GradientPaint GraphicAttribute Graphics Graphics2D GraphicsConfigTemplate GraphicsConfiguration GraphicsDevice GraphicsEnvironment GrayFilter GregorianCalendar GridBagConstraints GridBagLayout GridLayout Group Guard GuardedObject GZIPInputStream GZIPOutputStream Handler HandshakeCompletedEvent HandshakeCompletedListener HasControls HashAttributeSet HashDocAttributeSet HashMap HashPrintJobAttributeSet HashPrintRequestAttributeSet HashPrintServiceAttributeSet HashSet Hashtable HeadlessException HierarchyBoundsAdapter HierarchyBoundsListener HierarchyEvent HierarchyListener Highlighter HostnameVerifier HTML HTMLDocument HTMLEditorKit HTMLFrameHyperlinkEvent HTMLWriter HttpRetryException HttpsURLConnection HttpURLConnection HyperlinkEvent HyperlinkListener ICC_ColorSpace ICC_Profile ICC_ProfileGray ICC_ProfileRGB Icon IconUIResource IconView Identity IdentityHashMap IdentityScope IIOByteBuffer IIOException IIOImage IIOInvalidTreeException IIOMetadata IIOMetadataController IIOMetadataFormat IIOMetadataFormatImpl IIOMetadataNode IIOParam IIOParamController IIOReadProgressListener IIOReadUpdateListener IIOReadWarningListener IIORegistry IIOServiceProvider IIOWriteProgressListener IIOWriteWarningListener IllegalAccessError IllegalAccessException IllegalArgumentException IllegalBlockingModeException IllegalBlockSizeException IllegalCharsetNameException IllegalClassFormatException IllegalComponentStateException IllegalFormatCodePointException IllegalFormatConversionException IllegalFormatException IllegalFormatFlagsException IllegalFormatPrecisionException IllegalFormatWidthException IllegalMonitorStateException IllegalPathStateException IllegalSelectorException IllegalStateException IllegalThreadStateException Image ImageCapabilities ImageConsumer ImageFilter ImageGraphicAttribute ImageIcon ImageInputStream ImageInputStreamImpl ImageInputStreamSpi ImageIO ImageObserver ImageOutputStream ImageOutputStreamImpl ImageOutputStreamSpi ImageProducer ImageReader ImageReaderSpi ImageReaderWriterSpi ImageReadParam ImageTranscoder ImageTranscoderSpi ImageTypeSpecifier ImageView ImageWriteParam ImageWriter ImageWriterSpi ImagingOpException IncompatibleClassChangeError IncompleteAnnotationException IndexColorModel IndexedPropertyChangeEvent IndexedPropertyDescriptor IndexOutOfBoundsException Inet4Address Inet6Address InetAddress InetSocketAddress Inflater InflaterInputStream InheritableThreadLocal Inherited InitialContext InitialContextFactory InitialContextFactoryBuilder InitialDirContext InitialLdapContext InlineView InputContext InputEvent InputMap InputMapUIResource InputMethod InputMethodContext InputMethodDescriptor InputMethodEvent InputMethodHighlight InputMethodListener InputMethodRequests InputMismatchException InputStream InputStreamReader InputSubset InputVerifier Insets InsetsUIResource InstanceAlreadyExistsException InstanceNotFoundException InstantiationError InstantiationException Instrument Instrumentation InsufficientResourcesException IntBuffer Integer IntegerSyntax InternalError InternalFrameAdapter InternalFrameEvent InternalFrameFocusTraversalPolicy InternalFrameListener InternalFrameUI InternationalFormatter InterruptedException InterruptedIOException InterruptedNamingException InterruptibleChannel IntrospectionException Introspector InvalidActivityException InvalidAlgorithmParameterException InvalidApplicationException InvalidAttributeIdentifierException InvalidAttributesException InvalidAttributeValueException InvalidClassException InvalidDnDOperationException InvalidKeyException InvalidKeySpecException InvalidMarkException InvalidMidiDataException InvalidNameException InvalidObjectException InvalidOpenTypeException InvalidParameterException InvalidParameterSpecException InvalidPreferencesFormatException InvalidPropertiesFormatException InvalidRelationIdException InvalidRelationServiceException InvalidRelationTypeException InvalidRoleInfoException InvalidRoleValueException InvalidSearchControlsException InvalidSearchFilterException InvalidTargetObjectTypeException InvalidTransactionException InvocationEvent InvocationHandler InvocationTargetException IOException ItemEvent ItemListener ItemSelectable Iterable Iterator IvParameterSpec JApplet JarEntry JarException JarFile JarInputStream JarOutputStream JarURLConnection JButton JCheckBox JCheckBoxMenuItem JColorChooser JComboBox JComponent JdbcRowSet JDesktopPane JDialog JEditorPane JFileChooser JFormattedTextField JFrame JInternalFrame JLabel JLayeredPane JList JMenu JMenuBar JMenuItem JMException JMRuntimeException JMXAuthenticator JMXConnectionNotification JMXConnector JMXConnectorFactory JMXConnectorProvider JMXConnectorServer JMXConnectorServerFactory JMXConnectorServerMBean JMXConnectorServerProvider JMXPrincipal JMXProviderException JMXServerErrorException JMXServiceURL JobAttributes JobHoldUntil JobImpressions JobImpressionsCompleted JobImpressionsSupported JobKOctets JobKOctetsProcessed JobKOctetsSupported JobMediaSheets JobMediaSheetsCompleted JobMediaSheetsSupported JobMessageFromOperator JobName JobOriginatingUserName JobPriority JobPrioritySupported JobSheets JobState JobStateReason JobStateReasons Joinable JoinRowSet JOptionPane JPanel JPasswordField JPEGHuffmanTable JPEGImageReadParam JPEGImageWriteParam JPEGQTable JPopupMenu JProgressBar JRadioButton JRadioButtonMenuItem JRootPane JScrollBar JScrollPane JSeparator JSlider JSpinner JSplitPane JTabbedPane JTable JTableHeader JTextArea JTextComponent JTextField JTextPane JToggleButton JToolBar JToolTip JTree JViewport JWindow KerberosKey KerberosPrincipal KerberosTicket Kernel Key KeyAdapter KeyAgreement KeyAgreementSpi KeyAlreadyExistsException KeyboardFocusManager KeyEvent KeyEventDispatcher KeyEventPostProcessor KeyException KeyFactory KeyFactorySpi KeyGenerator KeyGeneratorSpi KeyListener KeyManagementException KeyManager KeyManagerFactory KeyManagerFactorySpi Keymap KeyPair KeyPairGenerator KeyPairGeneratorSpi KeyRep KeySpec KeyStore KeyStoreBuilderParameters KeyStoreException KeyStoreSpi KeyStroke Label LabelUI LabelView LanguageCallback LastOwnerException LayeredHighlighter LayoutFocusTraversalPolicy LayoutManager LayoutManager2 LayoutQueue LDAPCertStoreParameters LdapContext LdapName LdapReferralException Lease Level LimitExceededException Line Line2D LineBorder LineBreakMeasurer LineEvent LineListener LineMetrics LineNumberInputStream LineNumberReader LineUnavailableException LinkageError LinkedBlockingQueue LinkedHashMap LinkedHashSet LinkedList LinkException LinkLoopException LinkRef List ListCellRenderer ListDataEvent ListDataListener ListenerNotFoundException ListIterator ListModel ListResourceBundle ListSelectionEvent ListSelectionListener ListSelectionModel ListUI ListView LoaderHandler Locale LocateRegistry Lock LockSupport Logger LoggingMXBean LoggingPermission LoginContext LoginException LoginModule LogManager LogRecord LogStream Long LongBuffer LookAndFeel LookupOp LookupTable Mac MacSpi MalformedInputException MalformedLinkException MalformedObjectNameException MalformedParameterizedTypeException MalformedURLException ManagementFactory ManagementPermission ManageReferralControl ManagerFactoryParameters Manifest Map MappedByteBuffer MarshalException MarshalledObject MaskFormatter Matcher MatchResult Math MathContext MatteBorder MBeanAttributeInfo MBeanConstructorInfo MBeanException MBeanFeatureInfo MBeanInfo MBeanNotificationInfo MBeanOperationInfo MBeanParameterInfo MBeanPermission MBeanRegistration MBeanRegistrationException MBeanServer MBeanServerBuilder MBeanServerConnection MBeanServerDelegate MBeanServerDelegateMBean MBeanServerFactory MBeanServerForwarder MBeanServerInvocationHandler MBeanServerNotification MBeanServerNotificationFilter MBeanServerPermission MBeanTrustPermission Media MediaName MediaPrintableArea MediaSize MediaSizeName MediaTracker MediaTray Member MemoryCacheImageInputStream MemoryCacheImageOutputStream MemoryHandler MemoryImageSource MemoryManagerMXBean MemoryMXBean MemoryNotificationInfo MemoryPoolMXBean MemoryType MemoryUsage Menu MenuBar MenuBarUI MenuComponent MenuContainer MenuDragMouseEvent MenuDragMouseListener MenuElement MenuEvent MenuItem MenuItemUI MenuKeyEvent MenuKeyListener MenuListener MenuSelectionManager MenuShortcut MessageDigest MessageDigestSpi MessageFormat MetaEventListener MetalBorders MetalButtonUI MetalCheckBoxIcon MetalCheckBoxUI MetalComboBoxButton MetalComboBoxEditor MetalComboBoxIcon MetalComboBoxUI MetalDesktopIconUI MetalFileChooserUI MetalIconFactory MetalInternalFrameTitlePane MetalInternalFrameUI MetalLabelUI MetalLookAndFeel MetalMenuBarUI MetalPopupMenuSeparatorUI MetalProgressBarUI MetalRadioButtonUI MetalRootPaneUI MetalScrollBarUI MetalScrollButton MetalScrollPaneUI MetalSeparatorUI MetalSliderUI MetalSplitPaneUI MetalTabbedPaneUI MetalTextFieldUI MetalTheme MetalToggleButtonUI MetalToolBarUI MetalToolTipUI MetalTreeUI MetaMessage Method MethodDescriptor MGF1ParameterSpec MidiChannel MidiDevice MidiDeviceProvider MidiEvent MidiFileFormat MidiFileReader MidiFileWriter MidiMessage MidiSystem MidiUnavailableException MimeTypeParseException MinimalHTMLWriter MissingFormatArgumentException MissingFormatWidthException MissingResourceException Mixer MixerProvider MLet MLetMBean ModelMBean ModelMBeanAttributeInfo ModelMBeanConstructorInfo ModelMBeanInfo ModelMBeanInfoSupport ModelMBeanNotificationBroadcaster ModelMBeanNotificationInfo ModelMBeanOperationInfo ModificationItem Modifier Monitor MonitorMBean MonitorNotification MonitorSettingException MouseAdapter MouseDragGestureRecognizer MouseEvent MouseInfo MouseInputAdapter MouseInputListener MouseListener MouseMotionAdapter MouseMotionListener MouseWheelEvent MouseWheelListener MultiButtonUI MulticastSocket MultiColorChooserUI MultiComboBoxUI MultiDesktopIconUI MultiDesktopPaneUI MultiDoc MultiDocPrintJob MultiDocPrintService MultiFileChooserUI MultiInternalFrameUI MultiLabelUI MultiListUI MultiLookAndFeel MultiMenuBarUI MultiMenuItemUI MultiOptionPaneUI MultiPanelUI MultiPixelPackedSampleModel MultipleDocumentHandling MultipleMaster MultiPopupMenuUI MultiProgressBarUI MultiRootPaneUI MultiScrollBarUI MultiScrollPaneUI MultiSeparatorUI MultiSliderUI MultiSpinnerUI MultiSplitPaneUI MultiTabbedPaneUI MultiTableHeaderUI MultiTableUI MultiTextUI MultiToolBarUI MultiToolTipUI MultiTreeUI MultiViewportUI MutableAttributeSet MutableComboBoxModel MutableTreeNode Name NameAlreadyBoundException NameCallback NameClassPair NameNotFoundException NameParser NamespaceChangeListener NamespaceContext Naming NamingEnumeration NamingEvent NamingException NamingExceptionEvent NamingListener NamingManager NamingSecurityException NavigationFilter NegativeArraySizeException NetPermission NetworkInterface NoClassDefFoundError NoConnectionPendingException NodeChangeEvent NodeChangeListener NoInitialContextException NoninvertibleTransformException NonReadableChannelException NonWritableChannelException NoPermissionException NoRouteToHostException NoSuchAlgorithmException NoSuchAttributeException NoSuchElementException NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException NoSuchObjectException NoSuchPaddingException NoSuchProviderException NotActiveException NotBoundException NotCompliantMBeanException NotContextException Notification NotificationBroadcaster NotificationBroadcasterSupport NotificationEmitter NotificationFilter NotificationFilterSupport NotificationListener NotificationResult NotOwnerException NotSerializableException NotYetBoundException NotYetConnectedException NullCipher NullPointerException Number NumberFormat NumberFormatException NumberFormatter NumberOfDocuments NumberOfInterveningJobs NumberUp NumberUpSupported NumericShaper OAEPParameterSpec Object ObjectChangeListener ObjectFactory ObjectFactoryBuilder ObjectInput ObjectInputStream ObjectInputValidation ObjectInstance ObjectName ObjectOutput ObjectOutputStream ObjectStreamClass ObjectStreamConstants ObjectStreamException ObjectStreamField ObjectView ObjID Observable Observer OceanTheme OpenDataException OpenMBeanAttributeInfo OpenMBeanAttributeInfoSupport OpenMBeanConstructorInfo OpenMBeanConstructorInfoSupport OpenMBeanInfo OpenMBeanInfoSupport OpenMBeanOperationInfo OpenMBeanOperationInfoSupport OpenMBeanParameterInfo OpenMBeanParameterInfoSupport OpenType OperatingSystemMXBean Operation OperationNotSupportedException OperationsException Option OptionalDataException OptionPaneUI OrientationRequested OutOfMemoryError OutputDeviceAssigned OutputKeys OutputStream OutputStreamWriter OverlappingFileLockException OverlayLayout Override Owner Pack200 Package PackedColorModel Pageable PageAttributes PagedResultsControl PagedResultsResponseControl PageFormat PageRanges PagesPerMinute PagesPerMinuteColor Paint PaintContext PaintEvent Panel PanelUI Paper ParagraphView ParameterBlock ParameterDescriptor ParameterizedType ParameterMetaData ParseException ParsePosition Parser ParserConfigurationException ParserDelegator PartialResultException PasswordAuthentication PasswordCallback PasswordView Patch PathIterator Pattern PatternSyntaxException PBEKey PBEKeySpec PBEParameterSpec PDLOverrideSupported Permission PermissionCollection Permissions PersistenceDelegate PersistentMBean PhantomReference Pipe PipedInputStream PipedOutputStream PipedReader PipedWriter PixelGrabber PixelInterleavedSampleModel PKCS8EncodedKeySpec PKIXBuilderParameters PKIXCertPathBuilderResult PKIXCertPathChecker PKIXCertPathValidatorResult PKIXParameters PlainDocument PlainView Point Point2D PointerInfo Policy PolicyNode PolicyQualifierInfo Polygon PooledConnection Popup PopupFactory PopupMenu PopupMenuEvent PopupMenuListener PopupMenuUI Port PortableRemoteObject PortableRemoteObjectDelegate PortUnreachableException Position Predicate PreferenceChangeEvent PreferenceChangeListener Preferences PreferencesFactory PreparedStatement PresentationDirection Principal Printable PrinterAbortException PrinterException PrinterGraphics PrinterInfo PrinterIOException PrinterIsAcceptingJobs PrinterJob PrinterLocation PrinterMakeAndModel PrinterMessageFromOperator PrinterMoreInfo PrinterMoreInfoManufacturer PrinterName PrinterResolution PrinterState PrinterStateReason PrinterStateReasons PrinterURI PrintEvent PrintException PrintGraphics PrintJob PrintJobAdapter PrintJobAttribute PrintJobAttributeEvent PrintJobAttributeListener PrintJobAttributeSet PrintJobEvent PrintJobListener PrintQuality PrintRequestAttribute PrintRequestAttributeSet PrintService PrintServiceAttribute PrintServiceAttributeEvent PrintServiceAttributeListener PrintServiceAttributeSet PrintServiceLookup PrintStream PrintWriter PriorityBlockingQueue PriorityQueue PrivateClassLoader PrivateCredentialPermission PrivateKey PrivateMLet PrivilegedAction PrivilegedActionException PrivilegedExceptionAction Process ProcessBuilder ProfileDataException ProgressBarUI ProgressMonitor ProgressMonitorInputStream Properties PropertyChangeEvent PropertyChangeListener PropertyChangeListenerProxy PropertyChangeSupport PropertyDescriptor PropertyEditor PropertyEditorManager PropertyEditorSupport PropertyPermission PropertyResourceBundle PropertyVetoException ProtectionDomain ProtocolException Provider ProviderException Proxy ProxySelector PSource PSSParameterSpec PublicKey PushbackInputStream PushbackReader QName QuadCurve2D Query QueryEval QueryExp Queue QueuedJobCount Random RandomAccess RandomAccessFile Raster RasterFormatException RasterOp RC2ParameterSpec RC5ParameterSpec Rdn Readable ReadableByteChannel Reader ReadOnlyBufferException ReadWriteLock RealmCallback RealmChoiceCallback Receiver Rectangle Rectangle2D RectangularShape ReentrantLock ReentrantReadWriteLock Ref RefAddr Reference Referenceable ReferenceQueue ReferenceUriSchemesSupported ReferralException ReflectionException ReflectPermission Refreshable RefreshFailedException Region RegisterableService Registry RegistryHandler RejectedExecutionException RejectedExecutionHandler Relation RelationException RelationNotFoundException RelationNotification RelationService RelationServiceMBean RelationServiceNotRegisteredException RelationSupport RelationSupportMBean RelationType RelationTypeNotFoundException RelationTypeSupport Remote RemoteCall RemoteException RemoteObject RemoteObjectInvocationHandler RemoteRef RemoteServer RemoteStub RenderableImage RenderableImageOp RenderableImageProducer RenderContext RenderedImage RenderedImageFactory Renderer RenderingHints RepaintManager ReplicateScaleFilter RequestingUserName RequiredModelMBean RescaleOp ResolutionSyntax Resolver ResolveResult ResourceBundle ResponseCache Result ResultSet ResultSetMetaData Retention RetentionPolicy ReverbType RGBImageFilter RMIClassLoader RMIClassLoaderSpi RMIClientSocketFactory RMIConnection RMIConnectionImpl RMIConnectionImpl_Stub RMIConnector RMIConnectorServer RMIFailureHandler RMIIIOPServerImpl RMIJRMPServerImpl RMISecurityException RMISecurityManager RMIServer RMIServerImpl RMIServerImpl_Stub RMIServerSocketFactory RMISocketFactory Robot Role RoleInfo RoleInfoNotFoundException RoleList RoleNotFoundException RoleResult RoleStatus RoleUnresolved RoleUnresolvedList RootPaneContainer RootPaneUI RoundingMode RoundRectangle2D RowMapper RowSet RowSetEvent RowSetInternal RowSetListener RowSetMetaData RowSetMetaDataImpl RowSetReader RowSetWarning RowSetWriter RSAKey RSAKeyGenParameterSpec RSAMultiPrimePrivateCrtKey RSAMultiPrimePrivateCrtKeySpec RSAOtherPrimeInfo RSAPrivateCrtKey RSAPrivateCrtKeySpec RSAPrivateKey RSAPrivateKeySpec RSAPublicKey RSAPublicKeySpec RTFEditorKit RuleBasedCollator Runnable Runtime RuntimeErrorException RuntimeException RuntimeMBeanException RuntimeMXBean RuntimeOperationsException RuntimePermission SampleModel Sasl SaslClient SaslClientFactory SaslException SaslServer SaslServerFactory Savepoint SAXParser SAXParserFactory SAXResult SAXSource SAXTransformerFactory Scanner ScatteringByteChannel ScheduledExecutorService ScheduledFuture ScheduledThreadPoolExecutor Schema SchemaFactory SchemaFactoryLoader SchemaViolationException Scrollable Scrollbar ScrollBarUI ScrollPane ScrollPaneAdjustable ScrollPaneConstants ScrollPaneLayout ScrollPaneUI SealedObject SearchControls SearchResult SecretKey SecretKeyFactory SecretKeyFactorySpi SecretKeySpec SecureCacheResponse SecureClassLoader SecureRandom SecureRandomSpi Security SecurityException SecurityManager SecurityPermission Segment SelectableChannel SelectionKey Selector SelectorProvider Semaphore SeparatorUI Sequence SequenceInputStream Sequencer SerialArray SerialBlob SerialClob SerialDatalink SerialException Serializable SerializablePermission SerialJavaObject SerialRef SerialStruct ServerCloneException ServerError ServerException ServerNotActiveException ServerRef ServerRuntimeException ServerSocket ServerSocketChannel ServerSocketFactory ServiceNotFoundException ServicePermission ServiceRegistry ServiceUI ServiceUIFactory ServiceUnavailableException Set SetOfIntegerSyntax Severity Shape ShapeGraphicAttribute SheetCollate Short ShortBuffer ShortBufferException ShortLookupTable ShortMessage Sides Signature SignatureException SignatureSpi SignedObject Signer SimpleAttributeSet SimpleBeanInfo SimpleDateFormat SimpleDoc SimpleFormatter SimpleTimeZone SimpleType SinglePixelPackedSampleModel SingleSelectionModel Size2DSyntax SizeLimitExceededException SizeRequirements SizeSequence Skeleton SkeletonMismatchException SkeletonNotFoundException SliderUI Socket SocketAddress SocketChannel SocketException SocketFactory SocketHandler SocketImpl SocketImplFactory SocketOptions SocketPermission SocketSecurityException SocketTimeoutException SoftBevelBorder SoftReference SortControl SortedMap SortedSet SortingFocusTraversalPolicy SortKey SortResponseControl Soundbank SoundbankReader SoundbankResource Source SourceDataLine SourceLocator SpinnerDateModel SpinnerListModel SpinnerModel SpinnerNumberModel SpinnerUI SplitPaneUI Spring SpringLayout SQLData SQLException SQLInput SQLInputImpl SQLOutput SQLOutputImpl SQLPermission SQLWarning SSLContext SSLContextSpi SSLEngine SSLEngineResult SSLException SSLHandshakeException SSLKeyException SSLPeerUnverifiedException SSLPermission SSLProtocolException SslRMIClientSocketFactory SslRMIServerSocketFactory SSLServerSocket SSLServerSocketFactory SSLSession SSLSessionBindingEvent SSLSessionBindingListener SSLSessionContext SSLSocket SSLSocketFactory Stack StackOverflowError StackTraceElement StandardMBean StartTlsRequest StartTlsResponse StateEdit StateEditable StateFactory Statement StreamCorruptedException StreamHandler StreamPrintService StreamPrintServiceFactory StreamResult StreamSource StreamTokenizer StrictMath String StringBuffer StringBufferInputStream StringBuilder StringCharacterIterator StringContent StringIndexOutOfBoundsException StringMonitor StringMonitorMBean StringReader StringRefAddr StringSelection StringTokenizer StringValueExp StringWriter Stroke Struct Stub StubDelegate StubNotFoundException Style StyleConstants StyleContext StyledDocument StyledEditorKit StyleSheet Subject SubjectDelegationPermission SubjectDomainCombiner SupportedValuesAttribute SuppressWarnings SwingConstants SwingPropertyChangeSupport SwingUtilities SyncFactory SyncFactoryException SyncFailedException SynchronousQueue SyncProvider SyncProviderException SyncResolver SynthConstants SynthContext Synthesizer SynthGraphicsUtils SynthLookAndFeel SynthPainter SynthStyle SynthStyleFactory SysexMessage System SystemColor SystemFlavorMap TabableView TabbedPaneUI TabExpander TableCellEditor TableCellRenderer TableColumn TableColumnModel TableColumnModelEvent TableColumnModelListener TableHeaderUI TableModel TableModelEvent TableModelListener TableUI TableView TabSet TabStop TabularData TabularDataSupport TabularType TagElement Target TargetDataLine TargetedNotification Templates TemplatesHandler TextAction TextArea TextAttribute TextComponent TextEvent TextField TextHitInfo TextInputCallback TextLayout TextListener TextMeasurer TextOutputCallback TextSyntax TextUI TexturePaint Thread ThreadDeath ThreadFactory ThreadGroup ThreadInfo ThreadLocal ThreadMXBean ThreadPoolExecutor Throwable Tie TileObserver Time TimeLimitExceededException TimeoutException Timer TimerAlarmClockNotification TimerMBean TimerNotification TimerTask Timestamp TimeUnit TimeZone TitledBorder ToolBarUI Toolkit ToolTipManager ToolTipUI TooManyListenersException Track TransactionalWriter TransactionRequiredException TransactionRolledbackException Transferable TransferHandler TransformAttribute Transformer TransformerConfigurationException TransformerException TransformerFactory TransformerFactoryConfigurationError TransformerHandler Transmitter Transparency TreeCellEditor TreeCellRenderer TreeExpansionEvent TreeExpansionListener TreeMap TreeModel TreeModelEvent TreeModelListener TreeNode TreePath TreeSelectionEvent TreeSelectionListener TreeSelectionModel TreeSet TreeUI TreeWillExpandListener TrustAnchor TrustManager TrustManagerFactory TrustManagerFactorySpi Type TypeInfoProvider TypeNotPresentException Types TypeVariable UID UIDefaults UIManager UIResource UndeclaredThrowableException UndoableEdit UndoableEditEvent UndoableEditListener UndoableEditSupport UndoManager UnexpectedException UnicastRemoteObject UnknownError UnknownFormatConversionException UnknownFormatFlagsException UnknownGroupException UnknownHostException UnknownObjectException UnknownServiceException UnmappableCharacterException UnmarshalException UnmodifiableClassException UnmodifiableSetException UnrecoverableEntryException UnrecoverableKeyException Unreferenced UnresolvedAddressException UnresolvedPermission UnsatisfiedLinkError UnsolicitedNotification UnsolicitedNotificationEvent UnsolicitedNotificationListener UnsupportedAddressTypeException UnsupportedAudioFileException UnsupportedCallbackException UnsupportedCharsetException UnsupportedClassVersionError UnsupportedEncodingException UnsupportedFlavorException UnsupportedLookAndFeelException UnsupportedOperationException URI URIException URIResolver URISyntax URISyntaxException URL URLClassLoader URLConnection URLDecoder URLEncoder URLStreamHandler URLStreamHandlerFactory UTFDataFormatException Util UtilDelegate Utilities UUID Validator ValidatorHandler ValueExp ValueHandler ValueHandlerMultiFormat VariableHeightLayoutCache Vector VerifyError VetoableChangeListener VetoableChangeListenerProxy VetoableChangeSupport View ViewFactory ViewportLayout ViewportUI VirtualMachineError Visibility VMID VoiceStatus Void VolatileImage WeakHashMap WeakReference WebRowSet WildcardType Window WindowAdapter WindowConstants WindowEvent WindowFocusListener WindowListener WindowStateListener WrappedPlainView WritableByteChannel WritableRaster WritableRenderedImage WriteAbortedException Writer X500Principal X500PrivateCredential X509Certificate X509CertSelector X509CRL X509CRLEntry X509CRLSelector X509EncodedKeySpec X509ExtendedKeyManager X509Extension X509KeyManager X509TrustManager XAConnection XADataSource XAException XAResource Xid XMLConstants XMLDecoder XMLEncoder XMLFormatter XMLGregorianCalendar XMLParseException XmlReader XmlWriter XPath XPathConstants XPathException XPathExpression XPathExpressionException XPathFactory XPathFactoryConfigurationException XPathFunction XPathFunctionException XPathFunctionResolver XPathVariableResolver ZipEntry ZipException ZipFile ZipInputStream ZipOutputStream ZoneView ] #:nocov: end end end coderay-1.1.3/lib/coderay/scanners/java_script.rb000066400000000000000000000174201366440446100220200ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for JavaScript. # # Aliases: +ecmascript+, +ecma_script+, +javascript+ class JavaScript < Scanner register_for :java_script file_extension 'js' # The actual JavaScript keywords. KEYWORDS = %w[ break case catch continue default delete do else finally for function if in instanceof new return switch throw try typeof var void while with ] # :nodoc: PREDEFINED_CONSTANTS = %w[ false null true undefined NaN Infinity ] # :nodoc: MAGIC_VARIABLES = %w[ this arguments ] # :nodoc: arguments was introduced in JavaScript 1.4 KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[ case delete in instanceof new return throw typeof with ] # :nodoc: # Reserved for future use. RESERVED_WORDS = %w[ abstract boolean byte char class debugger double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile ] # :nodoc: IDENT_KIND = WordList.new(:ident). add(RESERVED_WORDS, :reserved). add(PREDEFINED_CONSTANTS, :predefined_constant). add(MAGIC_VARIABLES, :local_variable). add(KEYWORDS, :keyword) # :nodoc: ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc: REGEXP_ESCAPE = / [bBdDsSwW] /x # :nodoc: STRING_CONTENT_PATTERN = { "'" => /[^\\']+/, '"' => /[^\\"]+/, '/' => /[^\\\/]+/, } # :nodoc: KEY_CHECK_PATTERN = { "'" => / (?> [^\\']* (?: \\. [^\\']* )* ) ' \s* : /mx, '"' => / (?> [^\\"]* (?: \\. [^\\"]* )* ) " \s* : /mx, } # :nodoc: protected def setup @state = :initial end def scan_tokens encoder, options state, string_delimiter = options[:state] || @state if string_delimiter encoder.begin_group state end value_expected = true key_expected = false function_expected = false until eos? case state when :initial if match = scan(/ \s+ | \\\n /x) value_expected = true if !value_expected && match.index(?\n) encoder.text_token match, :space elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .*() ) !mx) value_expected = true encoder.text_token match, :comment state = :open_multi_line_comment if self[1] elsif check(/\.?\d/) key_expected = value_expected = false if match = scan(/0[xX][0-9A-Fa-f]+/) encoder.text_token match, :hex elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/) encoder.text_token match, :octal elsif match = scan(/\d+[fF]|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/) encoder.text_token match, :float elsif match = scan(/\d+/) encoder.text_token match, :integer end elsif value_expected && match = scan(/<([[:alpha:]]\w*) (?: [^\/>]*\/> | .*?<\/\1>)/xim) # TODO: scan over nested tags xml_scanner.tokenize match, :tokens => encoder value_expected = false elsif match = scan(/ [-+*=<>?:;,!&^|(\[{~%]+ | \.(?!\d) /x) value_expected = true last_operator = match[-1] key_expected = (last_operator == ?{) || (last_operator == ?,) function_expected = false encoder.text_token match, :operator elsif match = scan(/ [)\]}]+ /x) function_expected = key_expected = value_expected = false encoder.text_token match, :operator elsif match = scan(/ [$a-zA-Z_][A-Za-z_0-9$]* /x) kind = IDENT_KIND[match] value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match] # TODO: labels if kind == :ident if match.index(?$) # $ allowed inside an identifier kind = :predefined elsif function_expected kind = :function elsif check(/\s*[=:]\s*function\b/) kind = :function elsif key_expected && check(/\s*:/) kind = :key end end function_expected = (kind == :keyword) && (match == 'function') key_expected = false encoder.text_token match, kind elsif match = scan(/["']/) if key_expected && check(KEY_CHECK_PATTERN[match]) state = :key else state = :string end encoder.begin_group state string_delimiter = match encoder.text_token match, :delimiter elsif value_expected && (match = scan(/\//)) encoder.begin_group :regexp state = :regexp string_delimiter = '/' encoder.text_token match, :delimiter elsif match = scan(/ \/ /x) value_expected = true key_expected = false encoder.text_token match, :operator else encoder.text_token getch, :error end when :string, :regexp, :key if match = scan(STRING_CONTENT_PATTERN[string_delimiter]) encoder.text_token match, :content elsif match = scan(/["'\/]/) encoder.text_token match, :delimiter if state == :regexp modifiers = scan(/[gim]+/) encoder.text_token modifiers, :modifier if modifiers && !modifiers.empty? end encoder.end_group state string_delimiter = nil key_expected = value_expected = false state = :initial elsif state != :regexp && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)) if string_delimiter == "'" && !(match == "\\\\" || match == "\\'") encoder.text_token match, :content else encoder.text_token match, :char end elsif state == :regexp && match = scan(/ \\ (?: #{ESCAPE} | #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox) encoder.text_token match, :char elsif match = scan(/\\./m) encoder.text_token match, :content elsif match = scan(/ \\ | $ /x) encoder.end_group state encoder.text_token match, :error unless match.empty? string_delimiter = nil key_expected = value_expected = false state = :initial else raise_inspect "else case #{string_delimiter} reached; %p not handled." % peek(1), encoder end when :open_multi_line_comment if match = scan(%r! .*? \*/ !mx) state = :initial else match = scan(%r! .+ !mx) end value_expected = true encoder.text_token match, :comment if match else #:nocov: raise_inspect 'Unknown state: %p' % [state], encoder #:nocov: end end if options[:keep_state] @state = state, string_delimiter end if [:string, :regexp].include? state encoder.end_group state end encoder end protected def reset_instance super @xml_scanner.reset if defined? @xml_scanner end def xml_scanner @xml_scanner ||= CodeRay.scanner :xml, :tokens => @tokens, :keep_tokens => true, :keep_state => false end end end end coderay-1.1.3/lib/coderay/scanners/json.rb000066400000000000000000000052561366440446100204700ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for JSON (JavaScript Object Notation). class JSON < Scanner register_for :json file_extension 'json' KINDS_NOT_LOC = [ :float, :char, :content, :delimiter, :error, :integer, :operator, :value, ] # :nodoc: ESCAPE = / [bfnrt\\"\/] /x # :nodoc: UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # :nodoc: KEY = / (?> (?: [^\\"]+ | \\. )* ) " \s* : /x protected def setup @state = :initial end # See http://json.org/ for a definition of the JSON lexic/grammar. def scan_tokens encoder, options state = options[:state] || @state if [:string, :key].include? state encoder.begin_group state end until eos? case state when :initial if match = scan(/ \s+ /x) encoder.text_token match, :space elsif match = scan(/"/) state = check(/#{KEY}/o) ? :key : :string encoder.begin_group state encoder.text_token match, :delimiter elsif match = scan(/ [:,\[{\]}] /x) encoder.text_token match, :operator elsif match = scan(/ true | false | null /x) encoder.text_token match, :value elsif match = scan(/ -? (?: 0 | [1-9]\d* ) /x) if scan(/ \.\d+ (?:[eE][-+]?\d+)? | [eE][-+]? \d+ /x) match << matched encoder.text_token match, :float else encoder.text_token match, :integer end else encoder.text_token getch, :error end when :string, :key if match = scan(/[^\\"]+/) encoder.text_token match, :content elsif match = scan(/"/) encoder.text_token match, :delimiter encoder.end_group state state = :initial elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) encoder.text_token match, :char elsif match = scan(/\\./m) encoder.text_token match, :content elsif match = scan(/ \\ | $ /x) encoder.end_group state encoder.text_token match, :error unless match.empty? state = :initial else raise_inspect "else case \" reached; %p not handled." % peek(1), encoder end else raise_inspect 'Unknown state: %p' % [state], encoder end end if options[:keep_state] @state = state end if [:string, :key].include? state encoder.end_group state end encoder end end end end coderay-1.1.3/lib/coderay/scanners/lua.rb000066400000000000000000000257101366440446100202750ustar00rootroot00000000000000# encoding: utf-8 module CodeRay module Scanners # Scanner for the Lua[http://lua.org] programming lanuage. # # The language’s complete syntax is defined in # {the Lua manual}[http://www.lua.org/manual/5.2/manual.html], # which is what this scanner tries to conform to. class Lua < Scanner register_for :lua file_extension 'lua' title 'Lua' # Keywords used in Lua. KEYWORDS = %w[and break do else elseif end for function goto if in local not or repeat return then until while ] # Constants set by the Lua core. PREDEFINED_CONSTANTS = %w[false true nil] # The expressions contained in this array are parts of Lua’s `basic' # library. Although it’s not entirely necessary to load that library, # it is highly recommended and one would have to provide own implementations # of some of these expressions if one does not do so. They however aren’t # keywords, neither are they constants, but nearly predefined, so they # get tagged as `predefined' rather than anything else. # # This list excludes values of form `_UPPERCASE' because the Lua manual # requires such identifiers to be reserved by Lua anyway and they are # highlighted directly accordingly, without the need for specific # identifiers to be listed here. PREDEFINED_EXPRESSIONS = %w[ assert collectgarbage dofile error getmetatable ipairs load loadfile next pairs pcall print rawequal rawget rawlen rawset select setmetatable tonumber tostring type xpcall ] # Automatic token kind selection for normal words. IDENT_KIND = CodeRay::WordList.new(:ident). add(KEYWORDS, :keyword). add(PREDEFINED_CONSTANTS, :predefined_constant). add(PREDEFINED_EXPRESSIONS, :predefined) protected # Scanner initialization. def setup @state = :initial @brace_depth = 0 end # CodeRay entry hook. Starts parsing. def scan_tokens(encoder, options) state = options[:state] || @state brace_depth = @brace_depth num_equals = nil until eos? case state when :initial if match = scan(/\-\-\[\=*\[/) #--[[ long (possibly multiline) comment ]] num_equals = match.count("=") # Number must match for comment end encoder.begin_group(:comment) encoder.text_token(match, :delimiter) state = :long_comment elsif match = scan(/--.*$/) # --Lua comment encoder.text_token(match, :comment) elsif match = scan(/\[=*\[/) # [[ long (possibly multiline) string ]] num_equals = match.count("=") # Number must match for string end encoder.begin_group(:string) encoder.text_token(match, :delimiter) state = :long_string elsif match = scan(/::\s*[a-zA-Z_][a-zA-Z0-9_]+\s*::/) # ::goto_label:: encoder.text_token(match, :label) elsif match = scan(/_[A-Z]+/) # _UPPERCASE are names reserved for Lua encoder.text_token(match, :predefined) elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # Normal letters (or letters followed by digits) kind = IDENT_KIND[match] # Extra highlighting for entities following certain keywords if kind == :keyword and match == "function" state = :function_expected elsif kind == :keyword and match == "goto" state = :goto_label_expected elsif kind == :keyword and match == "local" state = :local_var_expected end encoder.text_token(match, kind) elsif match = scan(/\{/) # Opening table brace { encoder.begin_group(:map) encoder.text_token(match, brace_depth >= 1 ? :inline_delimiter : :delimiter) brace_depth += 1 state = :map elsif match = scan(/\}/) # Closing table brace } if brace_depth == 1 brace_depth = 0 encoder.text_token(match, :delimiter) encoder.end_group(:map) elsif brace_depth == 0 # Mismatched brace encoder.text_token(match, :error) else brace_depth -= 1 encoder.text_token(match, :inline_delimiter) encoder.end_group(:map) state = :map end elsif match = scan(/["']/) # String delimiters " and ' encoder.begin_group(:string) encoder.text_token(match, :delimiter) start_delim = match state = :string # ↓Prefix hex number ←|→ decimal number elsif match = scan(/-? (?:0x\h* \. \h+ (?:p[+\-]?\d+)? | \d*\.\d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power encoder.text_token(match, :float) # ↓Prefix hex number ←|→ decimal number elsif match = scan(/-? (?:0x\h+ (?:p[+\-]?\d+)? | \d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power encoder.text_token(match, :integer) elsif match = scan(/[\+\-\*\/%^\#=~<>\(\)\[\]:;,] | \.(?!\d)/x) # Operators encoder.text_token(match, :operator) elsif match = scan(/\s+/) # Space encoder.text_token(match, :space) else # Invalid stuff. Note that Lua doesn’t accept multibyte chars outside of strings, hence these are also errors. encoder.text_token(getch, :error) end # It may be that we’re scanning a full-blown subexpression of a table # (tables can contain full expressions in parts). # If this is the case, return to :map scanning state. state = :map if state == :initial && brace_depth >= 1 when :function_expected if match = scan(/\(.*?\)/m) # x = function() # "Anonymous" function without explicit name encoder.text_token(match, :operator) state = :initial elsif match = scan(/[a-zA-Z_] (?:[a-zA-Z0-9_\.] (?!\.\d))* [\.\:]/x) # function tbl.subtbl.foo() | function tbl:foo() # Colon only allowed as last separator encoder.text_token(match, :ident) elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # function foo() encoder.text_token(match, :function) state = :initial elsif match = scan(/\s+/) # Between the `function' keyword and the ident may be any amount of whitespace encoder.text_token(match, :space) else encoder.text_token(getch, :error) state = :initial end when :goto_label_expected if match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) encoder.text_token(match, :label) state = :initial elsif match = scan(/\s+/) # Between the `goto' keyword and the label may be any amount of whitespace encoder.text_token(match, :space) else encoder.text_token(getch, :error) end when :local_var_expected if match = scan(/function/) # local function ... encoder.text_token(match, :keyword) state = :function_expected elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) encoder.text_token(match, :local_variable) elsif match = scan(/,/) encoder.text_token(match, :operator) elsif match = scan(/\=/) encoder.text_token(match, :operator) # After encountering the equal sign, arbitrary expressions are # allowed again, so just return to the main state for further # parsing. state = :initial elsif match = scan(/\n/) encoder.text_token(match, :space) state = :initial elsif match = scan(/\s+/) encoder.text_token(match, :space) else encoder.text_token(getch, :error) end when :long_comment if match = scan(/.*?(?=\]={#{num_equals}}\])/m) encoder.text_token(match, :content) delim = scan(/\]={#{num_equals}}\]/) encoder.text_token(delim, :delimiter) else # No terminator found till EOF encoder.text_token(rest, :error) terminate end encoder.end_group(:comment) state = :initial when :long_string if match = scan(/.*?(?=\]={#{num_equals}}\])/m) # Long strings do not interpret any escape sequences encoder.text_token(match, :content) delim = scan(/\]={#{num_equals}}\]/) encoder.text_token(delim, :delimiter) else # No terminator found till EOF encoder.text_token(rest, :error) terminate end encoder.end_group(:string) state = :initial when :string if match = scan(/[^\\#{start_delim}\n]+/) # Everything except \ and the start delimiter character is string content (newlines are only allowed if preceeded by \ or \z) encoder.text_token(match, :content) elsif match = scan(/\\(?:['"abfnrtv\\]|z\s*|x\h\h|\d{1,3}|\n)/m) encoder.text_token(match, :char) elsif match = scan(Regexp.compile(start_delim)) encoder.text_token(match, :delimiter) encoder.end_group(:string) state = :initial elsif match = scan(/\n/) # Lua forbids unescaped newlines in normal non-long strings encoder.text_token("\\n\n", :error) # Visually appealing error indicator--otherwise users may wonder whether the highlighter cannot highlight multine strings encoder.end_group(:string) state = :initial else encoder.text_token(getch, :error) end when :map if match = scan(/[,;]/) encoder.text_token(match, :operator) elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]* (?=\s*=)/x) encoder.text_token(match, :key) encoder.text_token(scan(/\s+/), :space) if check(/\s+/) encoder.text_token(scan(/\=/), :operator) state = :initial elsif match = scan(/\s+/m) encoder.text_token(match, :space) else # Note this clause doesn’t advance the scan pointer, it’s a kind of # "retry with other options" (the :initial state then of course # advances the pointer). state = :initial end else raise end end if options[:keep_state] @state = state end encoder.end_group :string if [:string].include? state brace_depth.times { encoder.end_group :map } encoder end end end end coderay-1.1.3/lib/coderay/scanners/php.rb000066400000000000000000000605521366440446100203060ustar00rootroot00000000000000# encoding: utf-8 module CodeRay module Scanners load :html # Scanner for PHP. # # Original by Stefan Walk. class PHP < Scanner register_for :php file_extension 'php' KINDS_NOT_LOC = HTML::KINDS_NOT_LOC protected def setup @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true end def reset_instance super @html_scanner.reset end module Words # :nodoc: # according to http://www.php.net/manual/en/reserved.keywords.php KEYWORDS = %w[ abstract and array as break case catch class clone const continue declare default do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final for foreach function global goto if implements interface instanceof namespace new or private protected public static switch throw try use var while xor cfunction old_function ] TYPES = %w[ int integer float double bool boolean string array object resource ] LANGUAGE_CONSTRUCTS = %w[ die echo empty exit eval include include_once isset list require require_once return print unset ] CLASSES = %w[ Directory stdClass __PHP_Incomplete_Class exception php_user_filter Closure ] # according to http://php.net/quickref.php on 2009-04-21; # all functions with _ excluded (module functions) and selected additional functions BUILTIN_FUNCTIONS = %w[ abs acos acosh addcslashes addslashes aggregate array arsort ascii2ebcdic asin asinh asort assert atan atan2 atanh basename bcadd bccomp bcdiv bcmod bcmul bcpow bcpowmod bcscale bcsqrt bcsub bin2hex bindec bindtextdomain bzclose bzcompress bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite calculhmac ceil chdir checkdate checkdnsrr chgrp chmod chop chown chr chroot clearstatcache closedir closelog compact constant copy cos cosh count crc32 crypt current date dcgettext dcngettext deaggregate decbin dechex decoct define defined deg2rad delete dgettext die dirname diskfreespace dl dngettext doubleval each ebcdic2ascii echo empty end ereg eregi escapeshellarg escapeshellcmd eval exec exit exp explode expm1 extract fclose feof fflush fgetc fgetcsv fgets fgetss file fileatime filectime filegroup fileinode filemtime fileowner fileperms filepro filesize filetype floatval flock floor flush fmod fnmatch fopen fpassthru fprintf fputcsv fputs fread frenchtojd fscanf fseek fsockopen fstat ftell ftok ftruncate fwrite getallheaders getcwd getdate getenv gethostbyaddr gethostbyname gethostbynamel getimagesize getlastmod getmxrr getmygid getmyinode getmypid getmyuid getopt getprotobyname getprotobynumber getrandmax getrusage getservbyname getservbyport gettext gettimeofday gettype glob gmdate gmmktime gmstrftime gregoriantojd gzclose gzcompress gzdecode gzdeflate gzencode gzeof gzfile gzgetc gzgets gzgetss gzinflate gzopen gzpassthru gzputs gzread gzrewind gzseek gztell gzuncompress gzwrite hash header hebrev hebrevc hexdec htmlentities htmlspecialchars hypot iconv idate implode include intval ip2long iptcembed iptcparse isset jddayofweek jdmonthname jdtofrench jdtogregorian jdtojewish jdtojulian jdtounix jewishtojd join jpeg2wbmp juliantojd key krsort ksort lcfirst lchgrp lchown levenshtein link linkinfo list localeconv localtime log log10 log1p long2ip lstat ltrim mail main max md5 metaphone mhash microtime min mkdir mktime msql natcasesort natsort next ngettext nl2br nthmac octdec opendir openlog ord overload pack passthru pathinfo pclose pfsockopen phpcredits phpinfo phpversion pi png2wbmp popen pos pow prev print printf putenv quotemeta rad2deg rand range rawurldecode rawurlencode readdir readfile readgzfile readline readlink realpath recode rename require reset rewind rewinddir rmdir round rsort rtrim scandir serialize setcookie setlocale setrawcookie settype sha1 shuffle signeurlpaiement sin sinh sizeof sleep snmpget snmpgetnext snmprealwalk snmpset snmpwalk snmpwalkoid sort soundex split spliti sprintf sqrt srand sscanf stat strcasecmp strchr strcmp strcoll strcspn strftime stripcslashes stripos stripslashes stristr strlen strnatcasecmp strnatcmp strncasecmp strncmp strpbrk strpos strptime strrchr strrev strripos strrpos strspn strstr strtok strtolower strtotime strtoupper strtr strval substr symlink syslog system tan tanh tempnam textdomain time tmpfile touch trim uasort ucfirst ucwords uksort umask uniqid unixtojd unlink unpack unserialize unset urldecode urlencode usleep usort vfprintf virtual vprintf vsprintf wordwrap array_change_key_case array_chunk array_combine array_count_values array_diff array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_fill array_fill_keys array_filter array_flip array_intersect array_intersect_assoc array_intersect_key array_intersect_uassoc array_intersect_ukey array_key_exists array_keys array_map array_merge array_merge_recursive array_multisort array_pad array_pop array_product array_push array_rand array_reduce array_reverse array_search array_shift array_slice array_splice array_sum array_udiff array_udiff_assoc array_udiff_uassoc array_uintersect array_uintersect_assoc array_uintersect_uassoc array_unique array_unshift array_values array_walk array_walk_recursive assert_options base_convert base64_decode base64_encode chunk_split class_exists class_implements class_parents count_chars debug_backtrace debug_print_backtrace debug_zval_dump error_get_last error_log error_reporting extension_loaded file_exists file_get_contents file_put_contents load_file func_get_arg func_get_args func_num_args function_exists get_browser get_called_class get_cfg_var get_class get_class_methods get_class_vars get_current_user get_declared_classes get_declared_interfaces get_defined_constants get_defined_functions get_defined_vars get_extension_funcs get_headers get_html_translation_table get_include_path get_included_files get_loaded_extensions get_magic_quotes_gpc get_magic_quotes_runtime get_meta_tags get_object_vars get_parent_class get_required_filesget_resource_type gc_collect_cycles gc_disable gc_enable gc_enabled halt_compiler headers_list headers_sent highlight_file highlight_string html_entity_decode htmlspecialchars_decode in_array include_once inclued_get_data is_a is_array is_binary is_bool is_buffer is_callable is_dir is_double is_executable is_file is_finite is_float is_infinite is_int is_integer is_link is_long is_nan is_null is_numeric is_object is_readable is_real is_resource is_scalar is_soap_fault is_string is_subclass_of is_unicode is_uploaded_file is_writable is_writeable locale_get_default locale_set_default number_format override_function parse_str parse_url php_check_syntax php_ini_loaded_file php_ini_scanned_files php_logo_guid php_sapi_name php_strip_whitespace php_uname preg_filter preg_grep preg_last_error preg_match preg_match_all preg_quote preg_replace preg_replace_callback preg_split print_r require_once register_shutdown_function register_tick_function set_error_handler set_exception_handler set_file_buffer set_include_path set_magic_quotes_runtime set_time_limit shell_exec str_getcsv str_ireplace str_pad str_repeat str_replace str_rot13 str_shuffle str_split str_word_count strip_tags substr_compare substr_count substr_replace time_nanosleep time_sleep_until token_get_all token_name trigger_error unregister_tick_function use_soap_error_handler user_error utf8_decode utf8_encode var_dump var_export version_compare zend_logo_guid zend_thread_id zend_version create_function call_user_func_array posix_access posix_ctermid posix_get_last_error posix_getcwd posix_getegid posix_geteuid posix_getgid posix_getgrgid posix_getgrnam posix_getgroups posix_getlogin posix_getpgid posix_getpgrp posix_getpid posix_getppid posix_getpwnam posix_getpwuid posix_getrlimit posix_getsid posix_getuid posix_initgroups posix_isatty posix_kill posix_mkfifo posix_mknod posix_setegid posix_seteuid posix_setgid posix_setpgid posix_setsid posix_setuid posix_strerror posix_times posix_ttyname posix_uname pcntl_alarm pcntl_exec pcntl_fork pcntl_getpriority pcntl_setpriority pcntl_signal pcntl_signal_dispatch pcntl_sigprocmask pcntl_sigtimedwait pcntl_sigwaitinfo pcntl_wait pcntl_waitpid pcntl_wexitstatus pcntl_wifexited pcntl_wifsignaled pcntl_wifstopped pcntl_wstopsig pcntl_wtermsig ] # TODO: more built-in PHP functions? EXCEPTIONS = %w[ E_ERROR E_WARNING E_PARSE E_NOTICE E_CORE_ERROR E_CORE_WARNING E_COMPILE_ERROR E_COMPILE_WARNING E_USER_ERROR E_USER_WARNING E_USER_NOTICE E_DEPRECATED E_USER_DEPRECATED E_ALL E_STRICT ] CONSTANTS = %w[ null true false self parent __LINE__ __DIR__ __FILE__ __LINE__ __CLASS__ __NAMESPACE__ __METHOD__ __FUNCTION__ PHP_VERSION PHP_MAJOR_VERSION PHP_MINOR_VERSION PHP_RELEASE_VERSION PHP_VERSION_ID PHP_EXTRA_VERSION PHP_ZTS PHP_DEBUG PHP_MAXPATHLEN PHP_OS PHP_SAPI PHP_EOL PHP_INT_MAX PHP_INT_SIZE DEFAULT_INCLUDE_PATH PEAR_INSTALL_DIR PEAR_EXTENSION_DIR PHP_EXTENSION_DIR PHP_PREFIX PHP_BINDIR PHP_LIBDIR PHP_DATADIR PHP_SYSCONFDIR PHP_LOCALSTATEDIR PHP_CONFIG_FILE_PATH PHP_CONFIG_FILE_SCAN_DIR PHP_SHLIB_SUFFIX PHP_OUTPUT_HANDLER_START PHP_OUTPUT_HANDLER_CONT PHP_OUTPUT_HANDLER_END __COMPILER_HALT_OFFSET__ EXTR_OVERWRITE EXTR_SKIP EXTR_PREFIX_SAME EXTR_PREFIX_ALL EXTR_PREFIX_INVALID EXTR_PREFIX_IF_EXISTS EXTR_IF_EXISTS SORT_ASC SORT_DESC SORT_REGULAR SORT_NUMERIC SORT_STRING CASE_LOWER CASE_UPPER COUNT_NORMAL COUNT_RECURSIVE ASSERT_ACTIVE ASSERT_CALLBACK ASSERT_BAIL ASSERT_WARNING ASSERT_QUIET_EVAL CONNECTION_ABORTED CONNECTION_NORMAL CONNECTION_TIMEOUT INI_USER INI_PERDIR INI_SYSTEM INI_ALL M_E M_LOG2E M_LOG10E M_LN2 M_LN10 M_PI M_PI_2 M_PI_4 M_1_PI M_2_PI M_2_SQRTPI M_SQRT2 M_SQRT1_2 CRYPT_SALT_LENGTH CRYPT_STD_DES CRYPT_EXT_DES CRYPT_MD5 CRYPT_BLOWFISH DIRECTORY_SEPARATOR SEEK_SET SEEK_CUR SEEK_END LOCK_SH LOCK_EX LOCK_UN LOCK_NB HTML_SPECIALCHARS HTML_ENTITIES ENT_COMPAT ENT_QUOTES ENT_NOQUOTES INFO_GENERAL INFO_CREDITS INFO_CONFIGURATION INFO_MODULES INFO_ENVIRONMENT INFO_VARIABLES INFO_LICENSE INFO_ALL CREDITS_GROUP CREDITS_GENERAL CREDITS_SAPI CREDITS_MODULES CREDITS_DOCS CREDITS_FULLPAGE CREDITS_QA CREDITS_ALL STR_PAD_LEFT STR_PAD_RIGHT STR_PAD_BOTH PATHINFO_DIRNAME PATHINFO_BASENAME PATHINFO_EXTENSION PATH_SEPARATOR CHAR_MAX LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_ALL LC_MESSAGES ABDAY_1 ABDAY_2 ABDAY_3 ABDAY_4 ABDAY_5 ABDAY_6 ABDAY_7 DAY_1 DAY_2 DAY_3 DAY_4 DAY_5 DAY_6 DAY_7 ABMON_1 ABMON_2 ABMON_3 ABMON_4 ABMON_5 ABMON_6 ABMON_7 ABMON_8 ABMON_9 ABMON_10 ABMON_11 ABMON_12 MON_1 MON_2 MON_3 MON_4 MON_5 MON_6 MON_7 MON_8 MON_9 MON_10 MON_11 MON_12 AM_STR PM_STR D_T_FMT D_FMT T_FMT T_FMT_AMPM ERA ERA_YEAR ERA_D_T_FMT ERA_D_FMT ERA_T_FMT ALT_DIGITS INT_CURR_SYMBOL CURRENCY_SYMBOL CRNCYSTR MON_DECIMAL_POINT MON_THOUSANDS_SEP MON_GROUPING POSITIVE_SIGN NEGATIVE_SIGN INT_FRAC_DIGITS FRAC_DIGITS P_CS_PRECEDES P_SEP_BY_SPACE N_CS_PRECEDES N_SEP_BY_SPACE P_SIGN_POSN N_SIGN_POSN DECIMAL_POINT RADIXCHAR THOUSANDS_SEP THOUSEP GROUPING YESEXPR NOEXPR YESSTR NOSTR CODESET LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG LOG_KERN LOG_USER LOG_MAIL LOG_DAEMON LOG_AUTH LOG_SYSLOG LOG_LPR LOG_NEWS LOG_UUCP LOG_CRON LOG_AUTHPRIV LOG_LOCAL0 LOG_LOCAL1 LOG_LOCAL2 LOG_LOCAL3 LOG_LOCAL4 LOG_LOCAL5 LOG_LOCAL6 LOG_LOCAL7 LOG_PID LOG_CONS LOG_ODELAY LOG_NDELAY LOG_NOWAIT LOG_PERROR ] PREDEFINED = %w[ $GLOBALS $_SERVER $_GET $_POST $_FILES $_REQUEST $_SESSION $_ENV $_COOKIE $php_errormsg $HTTP_RAW_POST_DATA $http_response_header $argc $argv ] IDENT_KIND = WordList::CaseIgnoring.new(:ident). add(KEYWORDS, :keyword). add(TYPES, :predefined_type). add(LANGUAGE_CONSTRUCTS, :keyword). add(BUILTIN_FUNCTIONS, :predefined). add(CLASSES, :predefined_constant). add(EXCEPTIONS, :exception). add(CONSTANTS, :predefined_constant) VARIABLE_KIND = WordList.new(:local_variable). add(PREDEFINED, :predefined) end module RE # :nodoc: PHP_START = / ]*?language\s*=\s*"php"[^>]*?> | ]*?language\s*=\s*'php'[^>]*?> | <\?php\d? | <\?(?!xml) /xi PHP_END = %r! | \?> !xi HTML_INDICATOR = / ]/i IDENTIFIER = 'ä'[/[[:alpha:]]/] == 'ä' ? Regexp.new('[[:alpha:]_[^\0-\177]][[:alnum:]_[^\0-\177]]*') : Regexp.new('[a-z_\x7f-\xFF][a-z0-9_\x7f-\xFF]*', true) VARIABLE = /\$#{IDENTIFIER}/ OPERATOR = / \.(?!\d)=? | # dot that is not decimal point, string concatenation && | \|\| | # logic :: | -> | => | # scope, member, dictionary \\(?!\n) | # namespace \+\+ | -- | # increment, decrement [,;?:()\[\]{}] | # simple delimiters [-+*\/%&|^]=? | # ordinary math, binary logic, assignment shortcuts [~$] | # whatever =& | # reference assignment [=!]=?=? | <> | # comparison and assignment <<=? | >>=? | [<>]=? # comparison and shift /x end protected def scan_tokens encoder, options if check(RE::PHP_START) || # starts with #{RE::IDENTIFIER}/o) encoder.begin_group :inline encoder.text_token match, :local_variable encoder.text_token scan(/->/), :operator encoder.text_token scan(/#{RE::IDENTIFIER}/o), :ident encoder.end_group :inline elsif check(/->/) match << scan(/->/) encoder.text_token match, :error else encoder.text_token match, :local_variable end elsif match = scan(/\{/) if check(/\$/) encoder.begin_group :inline states[-1] = [states.last, delimiter] delimiter = nil states.push :php_inline encoder.text_token match, :delimiter else encoder.text_token match, :content end elsif match = scan(/\$\{#{RE::IDENTIFIER}\}/o) encoder.text_token match, :local_variable elsif match = scan(/\$/) encoder.text_token match, :content else encoder.end_group :string states.pop end when :class_expected if match = scan(/\s+/) encoder.text_token match, :space elsif match = scan(/#{RE::IDENTIFIER}/o) encoder.text_token match, :class states.pop else states.pop end when :function_expected if match = scan(/\s+/) encoder.text_token match, :space elsif match = scan(/&/) encoder.text_token match, :operator elsif match = scan(/#{RE::IDENTIFIER}/o) encoder.text_token match, :function states.pop else states.pop end else raise_inspect 'Unknown state!', encoder, states end end while state = states.pop encoder.end_group :string if [:sqstring, :dqstring].include? state if state.is_a? Array encoder.end_group :inline encoder.end_group :string if [:sqstring, :dqstring].include? state.first end end encoder end end end end coderay-1.1.3/lib/coderay/scanners/python.rb000066400000000000000000000236061366440446100210370ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for Python. Supports Python 3. # # Based on pygments' PythonLexer, see # http://dev.pocoo.org/projects/pygments/browser/pygments/lexers/agile.py. class Python < Scanner register_for :python file_extension 'py' KEYWORDS = [ 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield', 'nonlocal', # new in Python 3 ] # :nodoc: OLD_KEYWORDS = [ 'exec', 'print', # gone in Python 3 ] # :nodoc: PREDEFINED_METHODS_AND_TYPES = %w[ __import__ abs all any apply basestring bin bool buffer bytearray bytes callable chr classmethod cmp coerce compile complex delattr dict dir divmod enumerate eval execfile exit file filter float frozenset getattr globals hasattr hash hex id input int intern isinstance issubclass iter len list locals long map max min next object oct open ord pow property range raw_input reduce reload repr reversed round set setattr slice sorted staticmethod str sum super tuple type unichr unicode vars xrange zip ] # :nodoc: PREDEFINED_EXCEPTIONS = %w[ ArithmeticError AssertionError AttributeError BaseException DeprecationWarning EOFError EnvironmentError Exception FloatingPointError FutureWarning GeneratorExit IOError ImportError ImportWarning IndentationError IndexError KeyError KeyboardInterrupt LookupError MemoryError NameError NotImplemented NotImplementedError OSError OverflowError OverflowWarning PendingDeprecationWarning ReferenceError RuntimeError RuntimeWarning StandardError StopIteration SyntaxError SyntaxWarning SystemError SystemExit TabError TypeError UnboundLocalError UnicodeDecodeError UnicodeEncodeError UnicodeError UnicodeTranslateError UnicodeWarning UserWarning ValueError Warning ZeroDivisionError ] # :nodoc: PREDEFINED_VARIABLES_AND_CONSTANTS = [ 'False', 'True', 'None', # "keywords" since Python 3 'self', 'Ellipsis', 'NotImplemented', ] # :nodoc: IDENT_KIND = WordList.new(:ident). add(KEYWORDS, :keyword). add(OLD_KEYWORDS, :old_keyword). add(PREDEFINED_METHODS_AND_TYPES, :predefined). add(PREDEFINED_VARIABLES_AND_CONSTANTS, :predefined_constant). add(PREDEFINED_EXCEPTIONS, :exception) # :nodoc: NAME = / [[:alpha:]_] \w* /x # :nodoc: ESCAPE = / [abfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc: UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} | N\{[-\w ]+\} /x # :nodoc: OPERATOR = / \.\.\. | # ellipsis \.(?!\d) | # dot but not decimal point [,;:()\[\]{}] | # simple delimiters \/\/=? | \*\*=? | # special math [-+*\/%&|^]=? | # ordinary math and binary logic [~`] | # binary complement and inspection <<=? | >>=? | [<>=]=? | != # comparison and assignment /x # :nodoc: STRING_DELIMITER_REGEXP = Hash.new { |h, delimiter| h[delimiter] = Regexp.union delimiter # :nodoc: } STRING_CONTENT_REGEXP = Hash.new { |h, delimiter| h[delimiter] = / [^\\\n]+? (?= \\ | $ | #{Regexp.escape(delimiter)} ) /x # :nodoc: } DEF_NEW_STATE = WordList.new(:initial). add(%w(def), :def_expected). add(%w(import from), :include_expected). add(%w(class), :class_expected) # :nodoc: DESCRIPTOR = / #{NAME} (?: \. #{NAME} )* | \* /x # :nodoc: DOCSTRING_COMING = / [ \t]* u?r? ("""|''') /x # :nodoc: protected def scan_tokens encoder, options state = :initial string_delimiter = nil string_raw = false string_type = nil docstring_coming = match?(/#{DOCSTRING_COMING}/o) last_token_dot = false unicode = string.respond_to?(:encoding) && string.encoding.name == 'UTF-8' from_import_state = [] until eos? if state == :string if match = scan(STRING_DELIMITER_REGEXP[string_delimiter]) encoder.text_token match, :delimiter encoder.end_group string_type string_type = nil state = :initial next elsif string_delimiter.size == 3 && match = scan(/\n/) encoder.text_token match, :content elsif match = scan(STRING_CONTENT_REGEXP[string_delimiter]) encoder.text_token match, :content elsif !string_raw && match = scan(/ \\ #{ESCAPE} /ox) encoder.text_token match, :char elsif match = scan(/ \\ #{UNICODE_ESCAPE} /ox) encoder.text_token match, :char elsif match = scan(/ \\ . /x) encoder.text_token match, :content elsif match = scan(/ \\ | $ /x) encoder.end_group string_type string_type = nil encoder.text_token match, :error unless match.empty? state = :initial else raise_inspect "else case \" reached; %p not handled." % peek(1), encoder, state end elsif match = scan(/ [ \t]+ | \\?\n /x) encoder.text_token match, :space if match == "\n" state = :initial if state == :include_expected docstring_coming = true if match?(/#{DOCSTRING_COMING}/o) end next elsif match = scan(/ \# [^\n]* /mx) encoder.text_token match, :comment next elsif state == :initial if match = scan(/#{OPERATOR}/o) encoder.text_token match, :operator elsif match = scan(/(u?r?|b)?("""|"|'''|')/i) modifiers = self[1] string_delimiter = self[2] string_type = docstring_coming ? :docstring : (modifiers == 'b' ? :binary : :string) docstring_coming = false if docstring_coming encoder.begin_group string_type string_raw = false unless modifiers.empty? string_raw = !!modifiers.index(?r) encoder.text_token modifiers, :modifier match = string_delimiter end state = :string encoder.text_token match, :delimiter # TODO: backticks elsif match = scan(unicode ? /#{NAME}/uo : /#{NAME}/o) kind = IDENT_KIND[match] # TODO: keyword arguments kind = :ident if last_token_dot if kind == :old_keyword kind = check(/\(/) ? :ident : :keyword elsif kind == :predefined && check(/ *=/) kind = :ident elsif kind == :keyword state = DEF_NEW_STATE[match] from_import_state << match.to_sym if state == :include_expected end encoder.text_token match, kind elsif match = scan(/@[a-zA-Z0-9_.]+[lL]?/) encoder.text_token match, :decorator elsif match = scan(/0[xX][0-9A-Fa-f]+[lL]?/) encoder.text_token match, :hex elsif match = scan(/0[bB][01]+[lL]?/) encoder.text_token match, :binary elsif match = scan(/(?:\d*\.\d+|\d+\.\d*)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+/) if scan(/[jJ]/) match << matched encoder.text_token match, :imaginary else encoder.text_token match, :float end elsif match = scan(/0[oO][0-7]+|0[0-7]+(?![89.eE])[lL]?/) encoder.text_token match, :octal elsif match = scan(/\d+([lL])?/) if self[1] == nil && scan(/[jJ]/) match << matched encoder.text_token match, :imaginary else encoder.text_token match, :integer end else encoder.text_token getch, :error end elsif state == :def_expected state = :initial if match = scan(unicode ? /#{NAME}/uo : /#{NAME}/o) encoder.text_token match, :method else next end elsif state == :class_expected state = :initial if match = scan(unicode ? /#{NAME}/uo : /#{NAME}/o) encoder.text_token match, :class else next end elsif state == :include_expected if match = scan(unicode ? /#{DESCRIPTOR}/uo : /#{DESCRIPTOR}/o) if match == 'as' encoder.text_token match, :keyword from_import_state << :as elsif from_import_state.first == :from && match == 'import' encoder.text_token match, :keyword from_import_state << :import elsif from_import_state.last == :as # encoder.text_token match, match[0,1][unicode ? /[[:upper:]]/u : /[[:upper:]]/] ? :class : :method encoder.text_token match, :ident from_import_state.pop elsif IDENT_KIND[match] == :keyword unscan match = nil state = :initial next else encoder.text_token match, :include end elsif match = scan(/,/) from_import_state.pop if from_import_state.last == :as encoder.text_token match, :operator else from_import_state = [] state = :initial next end else raise_inspect 'Unknown state', encoder, state end last_token_dot = match == '.' end if state == :string encoder.end_group string_type end encoder end end end end coderay-1.1.3/lib/coderay/scanners/raydebug.rb000066400000000000000000000034441366440446100213160ustar00rootroot00000000000000require 'set' module CodeRay module Scanners # = Raydebug Scanner # # Highlights the output of the Encoders::Debug encoder. class Raydebug < Scanner register_for :raydebug file_extension 'raydebug' title 'CodeRay Token Dump' protected def setup super @known_token_kinds = TokenKinds.keys.map(&:to_s).to_set end def scan_tokens encoder, options opened_tokens = [] until eos? if match = scan(/\s+/) encoder.text_token match, :space elsif match = scan(/ (\w+) \( ( [^\)\\]* ( \\. [^\)\\]* )* ) /x) kind = self[1] encoder.text_token kind, :class encoder.text_token '(', :operator match = self[2] unless match.empty? if @known_token_kinds.include? kind encoder.text_token match, kind.to_sym else encoder.text_token match, :plain end end encoder.text_token match, :operator if match = scan(/\)/) elsif match = scan(/ (\w+) ([<\[]) /x) encoder.text_token self[1], :class if @known_token_kinds.include? self[1] kind = self[1].to_sym else kind = :unknown end opened_tokens << kind encoder.begin_group kind encoder.text_token self[2], :operator elsif !opened_tokens.empty? && match = scan(/ [>\]] /x) encoder.text_token match, :operator encoder.end_group opened_tokens.pop else encoder.text_token getch, :space end end encoder.end_group opened_tokens.pop until opened_tokens.empty? encoder end end end end coderay-1.1.3/lib/coderay/scanners/ruby.rb000066400000000000000000000426361366440446100205030ustar00rootroot00000000000000module CodeRay module Scanners # This scanner is really complex, since Ruby _is_ a complex language! # # It tries to highlight 100% of all common code, # and 90% of strange codes. # # It is optimized for HTML highlighting, and is not very useful for # parsing or pretty printing. class Ruby < Scanner register_for :ruby file_extension 'rb' autoload :Patterns, CodeRay.coderay_path('scanners', 'ruby', 'patterns') autoload :StringState, CodeRay.coderay_path('scanners', 'ruby', 'string_state') def interpreted_string_state StringState.new :string, true, '"' end protected def setup @state = :initial end def scan_tokens encoder, options state, heredocs = options[:state] || @state heredocs = heredocs.dup if heredocs.is_a?(Array) if state && state.instance_of?(StringState) encoder.begin_group state.type end last_state = nil method_call_expected = false value_expected = true inline_block_stack = nil inline_block_curly_depth = 0 if heredocs state = heredocs.shift encoder.begin_group state.type heredocs = nil if heredocs.empty? end # def_object_stack = nil # def_object_paren_depth = 0 patterns = Patterns # avoid constant lookup unicode = string.respond_to?(:encoding) && string.encoding.name == 'UTF-8' until eos? if state.instance_of? ::Symbol if match = scan(/[ \t\f\v]+/) encoder.text_token match, :space elsif match = scan(/\n/) if heredocs unscan # heredoc scanning needs \n at start state = heredocs.shift encoder.begin_group state.type heredocs = nil if heredocs.empty? else state = :initial if state == :undef_comma_expected encoder.text_token match, :space value_expected = true end elsif match = scan(bol? ? / \#(!)?.* | #{patterns::RUBYDOC_OR_DATA} /ox : /\#.*/) encoder.text_token match, self[1] ? :doctype : :comment elsif match = scan(/\\\n/) if heredocs unscan # heredoc scanning needs \n at start encoder.text_token scan(/\\/), :space state = heredocs.shift encoder.begin_group state.type heredocs = nil if heredocs.empty? else encoder.text_token match, :space end elsif state == :initial # IDENTS # if !method_call_expected && match = scan(unicode ? /#{patterns::METHOD_NAME}/uo : /#{patterns::METHOD_NAME}/o) kind = patterns::IDENT_KIND[match] if value_expected != :colon_expected && scan(/:(?!:)/) value_expected = true encoder.text_token match, :key encoder.text_token ':', :operator else value_expected = false if kind == :ident if match[/\A[A-Z]/] && !(match[/[!?]$/] || match?(/\(/)) kind = :constant end elsif kind == :keyword state = patterns::KEYWORD_NEW_STATE[match] if patterns::KEYWORDS_EXPECTING_VALUE[match] value_expected = match == 'when' ? :colon_expected : true end end value_expected = true if !value_expected && check(/#{patterns::VALUE_FOLLOWS}/o) encoder.text_token match, kind end elsif method_call_expected && match = scan(unicode ? /#{patterns::METHOD_AFTER_DOT}/uo : /#{patterns::METHOD_AFTER_DOT}/o) if method_call_expected == '::' && match[/\A[A-Z]/] && !match?(/\(/) encoder.text_token match, :constant else encoder.text_token match, :ident end method_call_expected = false value_expected = check(/#{patterns::VALUE_FOLLOWS}/o) # OPERATORS # elsif !method_call_expected && match = scan(/ (\.(?!\.)|::) | ( \.\.\.? | ==?=? | [,\(\[\{] ) | [\)\]\}] /x) method_call_expected = self[1] value_expected = !method_call_expected && !!self[2] if inline_block_stack case match when '{' inline_block_curly_depth += 1 when '}' inline_block_curly_depth -= 1 if inline_block_curly_depth == 0 # closing brace of inline block reached state, inline_block_curly_depth, heredocs = inline_block_stack.pop inline_block_stack = nil if inline_block_stack.empty? heredocs = nil if heredocs && heredocs.empty? encoder.text_token match, :inline_delimiter encoder.end_group :inline next end end end encoder.text_token match, :operator elsif match = scan(unicode ? /#{patterns::SYMBOL}/uo : /#{patterns::SYMBOL}/o) case delim = match[1] when ?', ?" encoder.begin_group :symbol encoder.text_token ':', :symbol match = delim.chr encoder.text_token match, :delimiter state = self.class::StringState.new :symbol, delim == ?", match else encoder.text_token match, :symbol value_expected = false end elsif match = scan(/ ' (?:(?>[^'\\]*) ')? | " (?:(?>[^"\\\#]*) ")? /mx) if match.size == 1 kind = check(self.class::StringState.simple_key_pattern(match)) ? :key : :string encoder.begin_group kind encoder.text_token match, :delimiter state = self.class::StringState.new kind, match == '"', match # important for streaming else kind = value_expected == true && scan(/:/) ? :key : :string encoder.begin_group kind encoder.text_token match[0,1], :delimiter encoder.text_token match[1..-2], :content if match.size > 2 encoder.text_token match[-1,1], :delimiter encoder.end_group kind encoder.text_token ':', :operator if kind == :key value_expected = false end elsif match = scan(unicode ? /#{patterns::INSTANCE_VARIABLE}/uo : /#{patterns::INSTANCE_VARIABLE}/o) value_expected = false encoder.text_token match, :instance_variable elsif value_expected && match = scan(/\//) encoder.begin_group :regexp encoder.text_token match, :delimiter state = self.class::StringState.new :regexp, true, '/' elsif match = scan(value_expected ? /[-+]?#{patterns::NUMERIC}/o : /#{patterns::NUMERIC}/o) if method_call_expected encoder.text_token match, :error method_call_expected = false else kind = self[1] ? :float : :integer # TODO: send :hex/:octal/:binary match << 'r' if match !~ /e/i && scan(/r/) match << 'i' if scan(/i/) encoder.text_token match, kind end value_expected = false elsif match = scan(/ [-+!~^\/]=? | [:;] | &\. | [*|&]{1,2}=? | >>? /x) value_expected = true encoder.text_token match, :operator elsif value_expected && match = scan(/#{patterns::HEREDOC_OPEN}/o) quote = self[3] delim = self[quote ? 4 : 2] kind = patterns::QUOTE_TO_TYPE[quote] encoder.begin_group kind encoder.text_token match, :delimiter encoder.end_group kind heredocs ||= [] # create heredocs if empty heredocs << self.class::StringState.new(kind, quote != "'", delim, self[1] ? :indented : :linestart) value_expected = false elsif value_expected && match = scan(/#{patterns::FANCY_STRING_START}/o) kind = patterns::FANCY_STRING_KIND[self[1]] encoder.begin_group kind state = self.class::StringState.new kind, patterns::FANCY_STRING_INTERPRETED[self[1]], self[2] encoder.text_token match, :delimiter elsif value_expected && match = scan(/#{patterns::CHARACTER}/o) value_expected = false encoder.text_token match, :integer elsif match = scan(/ %=? | <(?:<|=>?)? | \? /x) value_expected = match == '?' ? :colon_expected : true encoder.text_token match, :operator elsif match = scan(/`/) encoder.begin_group :shell encoder.text_token match, :delimiter state = self.class::StringState.new :shell, true, match elsif match = scan(unicode ? /#{patterns::GLOBAL_VARIABLE}/uo : /#{patterns::GLOBAL_VARIABLE}/o) encoder.text_token match, :global_variable value_expected = false elsif match = scan(unicode ? /#{patterns::CLASS_VARIABLE}/uo : /#{patterns::CLASS_VARIABLE}/o) encoder.text_token match, :class_variable value_expected = false elsif match = scan(/\\\z/) encoder.text_token match, :space else if method_call_expected method_call_expected = false next end unless unicode # check for unicode $DEBUG_BEFORE, $DEBUG = $DEBUG, false begin if check(/./mu).size > 1 # seems like we should try again with unicode unicode = true end rescue # bad unicode char; use getch ensure $DEBUG = $DEBUG_BEFORE end next if unicode end encoder.text_token getch, :error end if last_state state = last_state unless state.is_a?(StringState) # otherwise, a simple 'def"' results in unclosed tokens last_state = nil end elsif state == :def_expected if match = scan(unicode ? /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/uo : /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o) encoder.text_token match, :method state = :initial else last_state = :dot_expected state = :initial end elsif state == :dot_expected if match = scan(/\.|::/) # invalid definition state = :def_expected encoder.text_token match, :operator else state = :initial end elsif state == :module_expected if match = scan(/<#{patterns::METHOD_NAME_EX})(?!\.|::)/uo : /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o) encoder.text_token match, :method elsif match = scan(/#{patterns::SYMBOL}/o) case delim = match[1] when ?', ?" encoder.begin_group :symbol encoder.text_token ':', :symbol match = delim.chr encoder.text_token match, :delimiter state = self.class::StringState.new :symbol, delim == ?", match state.next_state = :undef_comma_expected else encoder.text_token match, :symbol end else state = :initial end elsif state == :undef_comma_expected if match = scan(/,/) encoder.text_token match, :operator state = :undef_expected else state = :initial end elsif state == :alias_expected match = scan(unicode ? /(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/uo : /(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/o) if match encoder.text_token self[1], (self[1][0] == ?: ? :symbol : :method) encoder.text_token self[2], :space encoder.text_token self[3], (self[3][0] == ?: ? :symbol : :method) end state = :initial else #:nocov: raise_inspect 'Unknown state: %p' % [state], encoder #:nocov: end else # StringState match = scan_until(state.pattern) || scan_rest unless match.empty? encoder.text_token match, :content break if eos? end if state.heredoc && self[1] # end of heredoc match = getch match << scan_until(/$/) unless eos? encoder.text_token match, :delimiter unless match.empty? encoder.end_group state.type state = state.next_state next end case match = getch when state.delim if state.paren_depth state.paren_depth -= 1 if state.paren_depth > 0 encoder.text_token match, :content next end end encoder.text_token match, :delimiter if state.type == :regexp && !eos? match = scan(/#{patterns::REGEXP_MODIFIERS}/o) encoder.text_token match, :modifier unless match.empty? end encoder.end_group state.type value_expected = false state = state.next_state when '\\' if state.interpreted if esc = scan(/#{patterns::ESCAPE}/o) encoder.text_token match + esc, :char else encoder.text_token match, :error end else case esc = getch when nil encoder.text_token match, :content when state.delim, '\\' encoder.text_token match + esc, :char else encoder.text_token match + esc, :content end end when '#' case peek(1) when '{' inline_block_stack ||= [] inline_block_stack << [state, inline_block_curly_depth, heredocs] value_expected = true state = :initial inline_block_curly_depth = 1 encoder.begin_group :inline encoder.text_token match + getch, :inline_delimiter when '$', '@' encoder.text_token match, :escape last_state = state state = :initial else #:nocov: raise_inspect 'else-case # reached; #%p not handled' % [peek(1)], encoder #:nocov: end when state.opening_paren state.paren_depth += 1 encoder.text_token match, :content else #:nocov raise_inspect 'else-case " reached; %p not handled, state = %p' % [match, state], encoder #:nocov: end end end # cleaning up if state.is_a? StringState encoder.end_group state.type end if options[:keep_state] if state.is_a?(StringState) && state.heredoc (heredocs ||= []).unshift state state = :initial elsif heredocs && heredocs.empty? heredocs = nil end @state = state, heredocs end if inline_block_stack until inline_block_stack.empty? state, = *inline_block_stack.pop encoder.end_group :inline encoder.end_group state.type end end encoder end end end end coderay-1.1.3/lib/coderay/scanners/ruby/000077500000000000000000000000001366440446100201435ustar00rootroot00000000000000coderay-1.1.3/lib/coderay/scanners/ruby/patterns.rb000066400000000000000000000115601366440446100223330ustar00rootroot00000000000000# encoding: utf-8 module CodeRay module Scanners module Ruby::Patterns # :nodoc: all KEYWORDS = %w[ 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 ] # See http://murfy.de/ruby-constants. PREDEFINED_CONSTANTS = %w[ nil true false self DATA ARGV ARGF ENV FALSE TRUE NIL STDERR STDIN STDOUT TOPLEVEL_BINDING RUBY_COPYRIGHT RUBY_DESCRIPTION RUBY_ENGINE RUBY_PATCHLEVEL RUBY_PLATFORM RUBY_RELEASE_DATE RUBY_REVISION RUBY_VERSION __FILE__ __LINE__ __ENCODING__ ] IDENT_KIND = WordList.new(:ident). add(KEYWORDS, :keyword). add(PREDEFINED_CONSTANTS, :predefined_constant) KEYWORD_NEW_STATE = WordList.new(:initial). add(%w[ def ], :def_expected). add(%w[ undef ], :undef_expected). add(%w[ alias ], :alias_expected). add(%w[ class module ], :module_expected) IDENT = 'ä'[/[[:alpha:]]/] == 'ä' ? Regexp.new('[[:alpha:]_[^\0-\177]][[:alnum:]_[^\0-\177]]*') : /[^\W\d]\w*/ METHOD_NAME = / #{IDENT} [?!]? /ox METHOD_NAME_OPERATOR = / \*\*? # multiplication and power | [-+~]@? # plus, minus, tilde with and without at sign | [\/%&|^`] # division, modulo or format strings, and, or, xor, system | \[\]=? # array getter and setter | << | >> # append or shift left, shift right | <=?>? | >=? # comparison, rocket operator | ===? | =~ # simple equality, case equality, match | ![~=@]? # negation with and without at sign, not-equal and not-match /ox METHOD_SUFFIX = / (?: [?!] | = (?![~>]|=(?!>)) ) /x METHOD_NAME_EX = / #{IDENT} #{METHOD_SUFFIX}? | #{METHOD_NAME_OPERATOR} /ox METHOD_AFTER_DOT = / #{IDENT} [?!]? | #{METHOD_NAME_OPERATOR} /ox INSTANCE_VARIABLE = / @ #{IDENT} /ox CLASS_VARIABLE = / @@ #{IDENT} /ox OBJECT_VARIABLE = / @@? #{IDENT} /ox GLOBAL_VARIABLE = / \$ (?: #{IDENT} | [1-9]\d* | 0\w* | [~&+`'=\/,;_.<>!@$?*":\\] | -[a-zA-Z_0-9] ) /ox PREFIX_VARIABLE = / #{GLOBAL_VARIABLE} | #{OBJECT_VARIABLE} /ox VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox QUOTE_TO_TYPE = { '`' => :shell, '/' => :regexp, } QUOTE_TO_TYPE.default = :string REGEXP_MODIFIERS = /[mousenix]*/ DECIMAL = /\d+(?:_\d+)*/ 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_SUFFIX = / #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? /ox FLOAT_OR_INT = / #{DECIMAL} (?: #{FLOAT_SUFFIX} () )? /ox NUMERIC = / (?: (?=0) (?: #{OCTAL} | #{HEXADECIMAL} | #{BINARY} ) | #{FLOAT_OR_INT} ) /ox SYMBOL = / : (?: #{METHOD_NAME_EX} | #{PREFIX_VARIABLE} | ['"] ) /ox METHOD_NAME_OR_SYMBOL = / #{METHOD_NAME_EX} | #{SYMBOL} /ox SIMPLE_ESCAPE = / [abefnrstv] | [0-7]{1,3} | x[0-9A-Fa-f]{1,2} | . /mx CONTROL_META_ESCAPE = / (?: M-|C-|c ) (?: \\ (?: M-|C-|c ) )* (?: [^\\] | \\ #{SIMPLE_ESCAPE} )? /mox ESCAPE = / #{CONTROL_META_ESCAPE} | #{SIMPLE_ESCAPE} /mox CHARACTER = / \? (?: [^\s\\] | \\ #{ESCAPE} ) /mox # NOTE: This is not completely correct, but # nobody needs heredoc delimiters ending with \n. HEREDOC_OPEN = / << ([-~])? # $1 = float (?: ( [A-Za-z_0-9]+ ) # $2 = delim | ( ["'`\/] ) # $3 = quote, type ( [^\n]*? ) \3 # $4 = delim ) /mx RUBYDOC = / =begin (?!\S) .*? (?: \Z | ^=end (?!\S) [^\n]* ) /mx DATA = / __END__$ .*? (?: \Z | (?=^\#CODE) ) /mx RUBYDOC_OR_DATA = / #{RUBYDOC} | #{DATA} /xo # Checks for a valid value to follow. This enables # value_expected in method calls without parentheses. VALUE_FOLLOWS = / (?>[ \t\f\v]+) (?: [%\/][^\s=] | <<-?\S | [-+] \d | #{CHARACTER} ) /ox KEYWORDS_EXPECTING_VALUE = WordList.new.add(%w[ and end in or unless begin defined? ensure redo super until break do next rescue then when case else for retry while elsif if not return yield ]) FANCY_STRING_START = / % ( [iIqQrswWx] | (?![a-zA-Z0-9]) ) ([^a-zA-Z0-9]) /x FANCY_STRING_KIND = Hash.new(:string).merge({ 'i' => :symbol, 'I' => :symbol, 'r' => :regexp, 's' => :symbol, 'x' => :shell, }) FANCY_STRING_INTERPRETED = Hash.new(true).merge({ 'i' => false, 'q' => false, 's' => false, 'w' => false, }) end end end coderay-1.1.3/lib/coderay/scanners/ruby/string_state.rb000066400000000000000000000045271366440446100232060ustar00rootroot00000000000000# encoding: utf-8 module CodeRay module Scanners class Ruby class StringState < Struct.new :type, :interpreted, :delim, :heredoc, :opening_paren, :paren_depth, :pattern, :next_state # :nodoc: all CLOSING_PAREN = Hash[ *%w[ ( ) [ ] < > { } ] ].each { |k,v| k.freeze; v.freeze } # debug, if I try to change it with << STRING_PATTERN = Hash.new do |h, k| delim, interpreted = *k delim_pattern = Regexp.escape(delim) if closing_paren = CLOSING_PAREN[delim] delim_pattern << Regexp.escape(closing_paren) end delim_pattern << '\\\\' unless delim == '\\' # special_escapes = # case interpreted # when :regexp_symbols # '| [|?*+(){}\[\].^$]' # end if interpreted && delim != '#' / (?= [#{delim_pattern}] | \# [{$@] ) /mx else / (?= [#{delim_pattern}] ) /mx end.tap do |pattern| h[k] = pattern if (delim.respond_to?(:ord) ? delim.ord : delim[0]) < 256 end end def self.simple_key_pattern delim if delim == "'" / (?> (?: [^\\']+ | \\. )* ) ' : /mx else / (?> (?: [^\\"\#]+ | \\. | \#\$[\\"] | \#\{[^\{\}]+\} | \#(?!\{) )* ) " : /mx end end def initialize kind, interpreted, delim, heredoc = false if heredoc pattern = heredoc_pattern delim, interpreted, heredoc == :indented delim = nil else pattern = STRING_PATTERN[ [delim, interpreted] ] if closing_paren = CLOSING_PAREN[delim] opening_paren = delim delim = closing_paren paren_depth = 1 end end super kind, interpreted, delim, heredoc, opening_paren, paren_depth, pattern, :initial end def heredoc_pattern delim, interpreted, indented # delim = delim.dup # workaround for old Ruby delim_pattern = Regexp.escape(delim) delim_pattern = / (?:\A|\n) #{ '(?>[ \t]*)' if indented } #{ Regexp.new delim_pattern } $ /x if interpreted / (?= #{delim_pattern}() | \\ | \# [{$@] ) /mx # $1 set == end of heredoc else / (?= #{delim_pattern}() | \\ ) /mx end end end end end end coderay-1.1.3/lib/coderay/scanners/sass.rb000066400000000000000000000172001366440446100204600ustar00rootroot00000000000000module CodeRay module Scanners # A scanner for Sass. class Sass < CSS register_for :sass file_extension 'sass' protected def setup @state = :initial end def scan_tokens encoder, options states = Array(options[:state] || @state).dup encoder.begin_group :string if states.last == :sqstring || states.last == :dqstring until eos? if bol? && (match = scan(/(?>( +)?(\/[\*\/])(.+)?)(?=\n)/)) encoder.text_token self[1], :space if self[1] encoder.begin_group :comment encoder.text_token self[2], :delimiter encoder.text_token self[3], :content if self[3] if match = scan(/(?:\n+#{self[1]} .*)+/) encoder.text_token match, :content end encoder.end_group :comment elsif match = scan(/\n|[^\n\S]+\n?/) encoder.text_token match, :space if match.index(/\n/) value_expected = false states.pop if states.last == :include end elsif states.last == :sass_inline && (match = scan(/\}/)) encoder.text_token match, :inline_delimiter encoder.end_group :inline states.pop elsif case states.last when :initial, :media, :sass_inline if match = scan(/(?>#{RE::Ident})(?!\()/ox) encoder.text_token match, value_expected ? :value : (check(/.*:(?![a-z])/) ? :key : :tag) next elsif !value_expected && (match = scan(/\*/)) encoder.text_token match, :tag next elsif match = scan(RE::Class) encoder.text_token match, :class next elsif match = scan(RE::Id) encoder.text_token match, :id next elsif match = scan(RE::PseudoClass) encoder.text_token match, :pseudo_class next elsif match = scan(RE::AttributeSelector) # TODO: Improve highlighting inside of attribute selectors. encoder.text_token match[0,1], :operator encoder.text_token match[1..-2], :attribute_name if match.size > 2 encoder.text_token match[-1,1], :operator if match[-1] == ?] next elsif match = scan(/(\=|@mixin +)#{RE::Ident}/o) encoder.text_token match, :function next elsif match = scan(/@import\b/) encoder.text_token match, :directive states << :include next elsif match = scan(/@media\b/) encoder.text_token match, :directive # states.push :media_before_name next end when :block if match = scan(/(?>#{RE::Ident})(?!\()/ox) if value_expected encoder.text_token match, :value else encoder.text_token match, :key end next end when :sqstring, :dqstring if match = scan(states.last == :sqstring ? /(?:[^\n\'\#]+|\\\n|#{RE::Escape}|#(?!\{))+/o : /(?:[^\n\"\#]+|\\\n|#{RE::Escape}|#(?!\{))+/o) encoder.text_token match, :content elsif match = scan(/['"]/) encoder.text_token match, :delimiter encoder.end_group :string states.pop elsif match = scan(/#\{/) encoder.begin_group :inline encoder.text_token match, :inline_delimiter states.push :sass_inline elsif match = scan(/ \\ | $ /x) encoder.end_group states.last encoder.text_token match, :error unless match.empty? states.pop else raise_inspect "else case #{states.last} reached; %p not handled." % peek(1), encoder end when :include if match = scan(/[^\s'",]+/) encoder.text_token match, :include next end else #:nocov: raise_inspect 'Unknown state: %p' % [states.last], encoder #:nocov: end elsif match = scan(/\$#{RE::Ident}/o) encoder.text_token match, :variable next elsif match = scan(/&/) encoder.text_token match, :local_variable elsif match = scan(/\+#{RE::Ident}/o) encoder.text_token match, :include value_expected = true elsif match = scan(/\/\*(?:.*?\*\/|.*)|\/\/.*/) encoder.text_token match, :comment elsif match = scan(/#\{/) encoder.begin_group :inline encoder.text_token match, :inline_delimiter states.push :sass_inline elsif match = scan(/\{/) value_expected = false encoder.text_token match, :operator states.push :block elsif match = scan(/\}/) value_expected = false encoder.text_token match, :operator if states.last == :block || states.last == :media states.pop end elsif match = scan(/['"]/) encoder.begin_group :string encoder.text_token match, :delimiter if states.include? :sass_inline # no nesting, just scan the string until delimiter content = scan_until(/(?=#{match}|\}|\z)/) encoder.text_token content, :content unless content.empty? encoder.text_token match, :delimiter if scan(/#{match}/) encoder.end_group :string else states.push match == "'" ? :sqstring : :dqstring end elsif match = scan(/#{RE::Function}/o) encoder.begin_group :function start = match[/^[-\w]+\(/] encoder.text_token start, :delimiter if match[-1] == ?) encoder.text_token match[start.size..-2], :content encoder.text_token ')', :delimiter else encoder.text_token match[start.size..-1], :content if start.size < match.size end encoder.end_group :function elsif match = scan(/[a-z][-a-z_]*(?=\()/o) encoder.text_token match, :predefined elsif match = scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox) encoder.text_token match, :float elsif match = scan(/#{RE::HexColor}/o) encoder.text_token match, :color elsif match = scan(/! *(?:important|optional)/) encoder.text_token match, :important elsif match = scan(/(?:rgb|hsl)a?\([^()\n]*\)?/) encoder.text_token match, :color elsif match = scan(/@else if\b|#{RE::AtKeyword}/o) encoder.text_token match, :directive value_expected = true elsif match = scan(/ == | != | [-+*\/>~:;,.=()] /x) if match == ':' value_expected = true elsif match == ';' value_expected = false end encoder.text_token match, :operator else encoder.text_token getch, :error end end states.pop if states.last == :include if options[:keep_state] @state = states.dup end while state = states.pop if state == :sass_inline encoder.end_group :inline elsif state == :sqstring || state == :dqstring encoder.end_group :string end end encoder end end end end coderay-1.1.3/lib/coderay/scanners/scanner.rb000066400000000000000000000225301366440446100211420ustar00rootroot00000000000000# encoding: utf-8 module CodeRay module Scanners # = Scanner # # The base class for all Scanners. # # It is a subclass of Ruby's great +StringScanner+, which # makes it easy to access the scanning methods inside. # # It is also +Enumerable+, so you can use it like an Array of # Tokens: # # require 'coderay' # # c_scanner = CodeRay::Scanners[:c].new "if (*p == '{') nest++;" # # for text, kind in c_scanner # puts text if kind == :operator # end # # # prints: (*==)++; # # OK, this is a very simple example :) # You can also use +map+, +any?+, +find+ and even +sort_by+, # if you want. class Scanner < StringScanner extend Plugin plugin_host Scanners # Raised if a Scanner fails while scanning ScanError = Class.new StandardError # The default options for all scanner classes. # # Define @default_options for subclasses. DEFAULT_OPTIONS = { } KINDS_NOT_LOC = [:comment, :doctype, :docstring] attr_accessor :state class << self # Normalizes the given code into a string with UNIX newlines, in the # scanner's internal encoding, with invalid and undefined charachters # replaced by placeholders. Always returns a new object. def normalize code # original = code code = code.to_s unless code.is_a? ::String return code if code.empty? if code.respond_to? :encoding code = encode_with_encoding code, self.encoding else code = to_unix code end # code = code.dup if code.eql? original code end # The typical filename suffix for this scanner's language. def file_extension extension = lang @file_extension ||= extension.to_s end # The encoding used internally by this scanner. def encoding name = 'UTF-8' @encoding ||= defined?(Encoding.find) && Encoding.find(name) end # The lang of this Scanner class, which is equal to its Plugin ID. def lang @plugin_id end protected def encode_with_encoding code, target_encoding if code.encoding == target_encoding if code.valid_encoding? return to_unix(code) else source_encoding = guess_encoding code end else source_encoding = code.encoding end # print "encode_with_encoding from #{source_encoding} to #{target_encoding}" code.encode target_encoding, source_encoding, :universal_newline => true, :undef => :replace, :invalid => :replace end def to_unix code code.index(?\r) ? code.gsub(/\r\n?/, "\n") : code end def guess_encoding s #:nocov: IO.popen("file -b --mime -", "w+") do |file| file.write s[0, 1024] file.close_write begin Encoding.find file.gets[/charset=([-\w]+)/, 1] rescue ArgumentError Encoding::BINARY end end #:nocov: end end # Create a new Scanner. # # * +code+ is the input String and is handled by the superclass # StringScanner. # * +options+ is a Hash with Symbols as keys. # It is merged with the default options of the class (you can # overwrite default options here.) # # Else, a Tokens object is used. def initialize code = '', options = {} if self.class == Scanner raise NotImplementedError, "I am only the basic Scanner class. I can't scan anything. :( Use my subclasses." end @options = self.class::DEFAULT_OPTIONS.merge options super self.class.normalize(code) @tokens = options[:tokens] || Tokens.new @tokens.scanner = self if @tokens.respond_to? :scanner= setup end # Sets back the scanner. Subclasses should redefine the reset_instance # method instead of this one. def reset super reset_instance end # Set a new string to be scanned. def string= code code = self.class.normalize(code) super code reset_instance end # the Plugin ID for this scanner def lang self.class.lang end # the default file extension for this scanner def file_extension self.class.file_extension end # Scan the code and returns all tokens in a Tokens object. def tokenize source = nil, options = {} options = @options.merge(options) set_tokens_from_options options set_string_from_source source begin scan_tokens @tokens, options rescue => e message = "Error in %s#scan_tokens, initial state was: %p" % [self.class, defined?(state) && state] raise_inspect e.message, @tokens, message, 30, e.backtrace end @cached_tokens = @tokens if source.is_a? Array @tokens.split_into_parts(*source.map { |part| part.size }) else @tokens end end # Cache the result of tokenize. def tokens @cached_tokens ||= tokenize end # Traverse the tokens. def each &block tokens.each(&block) end include Enumerable # The current line position of the scanner, starting with 1. # See also: #column. # # Beware, this is implemented inefficiently. It should be used # for debugging only. def line pos = self.pos return 1 if pos <= 0 binary_string[0...pos].count("\n") + 1 end # The current column position of the scanner, starting with 1. # See also: #line. def column pos = self.pos return 1 if pos <= 0 pos - (binary_string.rindex(?\n, pos - 1) || -1) end # The string in binary encoding. # # To be used with #pos, which is the index of the byte the scanner # will scan next. def binary_string @binary_string ||= if string.respond_to?(:bytesize) && string.bytesize != string.size #:nocov: string.dup.force_encoding('binary') #:nocov: else string end end protected # Can be implemented by subclasses to do some initialization # that has to be done once per instance. # # Use reset for initialization that has to be done once per # scan. def setup # :doc: end def set_string_from_source source case source when Array self.string = self.class.normalize(source.join) when nil reset else self.string = self.class.normalize(source) end end def set_tokens_from_options options @tokens = options[:tokens] || @tokens || Tokens.new @tokens.scanner = self if @tokens.respond_to? :scanner= end # This is the central method, and commonly the only one a # subclass implements. # # Subclasses must implement this method; it must return +tokens+ # and must only use Tokens#<< for storing scanned tokens! def scan_tokens tokens, options # :doc: raise NotImplementedError, "#{self.class}#scan_tokens not implemented." end # Resets the scanner. def reset_instance @tokens.clear if @tokens.respond_to?(:clear) && !@options[:keep_tokens] @cached_tokens = nil @binary_string = nil if defined? @binary_string end SCAN_ERROR_MESSAGE = <<-MESSAGE ***ERROR in %s: %s (after %s tokens) tokens: %s %s surrounding code: %p ~~ %p ***ERROR*** MESSAGE def raise_inspect_arguments message, tokens, state, ambit return File.basename(caller[0]), message, tokens_size(tokens), tokens_last(tokens, 10).map(&:inspect).join("\n"), scanner_state_info(state), binary_string[pos - ambit, ambit], binary_string[pos, ambit] end SCANNER_STATE_INFO = <<-INFO current line: %d column: %d pos: %d matched: %p state: %p bol?: %p, eos?: %p INFO def scanner_state_info state SCANNER_STATE_INFO % [ line, column, pos, matched, state || 'No state given!', bol?, eos?, ] end # Scanner error with additional status information def raise_inspect message, tokens, state = self.state, ambit = 30, backtrace = caller raise ScanError, SCAN_ERROR_MESSAGE % raise_inspect_arguments(message, tokens, state, ambit), backtrace end def tokens_size tokens tokens.size if tokens.respond_to?(:size) end def tokens_last tokens, n tokens.respond_to?(:last) ? tokens.last(n) : [] end # Shorthand for scan_until(/\z/). # This method also avoids a JRuby 1.9 mode bug. def scan_rest rest = self.rest terminate rest end end end end coderay-1.1.3/lib/coderay/scanners/sql.rb000066400000000000000000000125141366440446100203110ustar00rootroot00000000000000module CodeRay module Scanners # by Josh Goebel class SQL < Scanner register_for :sql KEYWORDS = %w( all and any as before begin between by case check collate each else end exists for foreign from full group having if in inner is join like not of on or order outer over references then to union using values when where left right distinct ) OBJECTS = %w( database databases table tables column columns fields index constraint constraints transaction function procedure row key view trigger ) COMMANDS = %w( add alter comment create delete drop grant insert into select update set show prompt begin commit rollback replace truncate ) PREDEFINED_TYPES = %w( char varchar varchar2 enum binary text tinytext mediumtext longtext blob tinyblob mediumblob longblob timestamp date time datetime year double decimal float int integer tinyint mediumint bigint smallint unsigned bit numeric bool boolean hex bin oct ) PREDEFINED_FUNCTIONS = %w( sum cast substring abs pi count min max avg now ) DIRECTIVES = %w( auto_increment unique default charset initially deferred deferrable cascade immediate read write asc desc after primary foreign return engine ) PREDEFINED_CONSTANTS = %w( null true false ) IDENT_KIND = WordList::CaseIgnoring.new(:ident). add(KEYWORDS, :keyword). add(OBJECTS, :type). add(COMMANDS, :class). add(PREDEFINED_TYPES, :predefined_type). add(PREDEFINED_CONSTANTS, :predefined_constant). add(PREDEFINED_FUNCTIONS, :predefined). add(DIRECTIVES, :directive) ESCAPE = / [rbfntv\n\\\/'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | . /mx UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x STRING_PREFIXES = /[xnb]|_\w+/i STRING_CONTENT_PATTERN = { '"' => / (?: [^\\"] | "" )+ /x, "'" => / (?: [^\\'] | '' )+ /x, '`' => / (?: [^\\`] | `` )+ /x, } def scan_tokens encoder, options state = :initial string_type = nil string_content = '' name_expected = false until eos? if state == :initial if match = scan(/ \s+ | \\\n /x) encoder.text_token match, :space elsif match = scan(/(?:--\s?|#).*/) encoder.text_token match, :comment elsif match = scan(%r( /\* (!)? (?: .*? \*/ | .* ) )mx) encoder.text_token match, self[1] ? :directive : :comment elsif match = scan(/ [*\/=<>:;,!&^|()\[\]{}~%] | [-+\.](?!\d) /x) name_expected = true if match == '.' && check(/[A-Za-z_]/) encoder.text_token match, :operator elsif match = scan(/(#{STRING_PREFIXES})?([`"'])/o) prefix = self[1] string_type = self[2] encoder.begin_group :string encoder.text_token prefix, :modifier if prefix match = string_type state = :string encoder.text_token match, :delimiter elsif match = scan(/ @? [A-Za-z_][A-Za-z_0-9\$]* /x) encoder.text_token match, name_expected ? :ident : (match[0] == ?@ ? :variable : IDENT_KIND[match]) name_expected = false elsif match = scan(/0[xX][0-9A-Fa-f]+/) encoder.text_token match, :hex elsif match = scan(/0[0-7]+(?![89.eEfF])/) encoder.text_token match, :octal elsif match = scan(/[-+]?(?>\d+)(?![.eEfF])/) encoder.text_token match, :integer elsif match = scan(/[-+]?(?:\d[fF]|\d*\.\d+(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+)/) encoder.text_token match, :float elsif match = scan(/\\N/) encoder.text_token match, :predefined_constant else encoder.text_token getch, :error end elsif state == :string if match = scan(STRING_CONTENT_PATTERN[string_type]) encoder.text_token match, :content elsif match = scan(/["'`]/) if string_type == match if peek(1) == string_type # doubling means escape encoder.text_token match + getch, :content else encoder.text_token match, :delimiter encoder.end_group :string state = :initial string_type = nil end else encoder.text_token match, :content end elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) encoder.text_token match, :char elsif match = scan(/ \\ . /mox) encoder.text_token match, :content elsif match = scan(/ \\ | $ /x) encoder.text_token match, :error unless match.empty? encoder.end_group :string state = :initial else raise "else case \" reached; %p not handled." % peek(1), encoder end else raise 'else-case reached', encoder end end if state == :string encoder.end_group state end encoder end end end end coderay-1.1.3/lib/coderay/scanners/taskpaper.rb000066400000000000000000000020101366440446100214720ustar00rootroot00000000000000module CodeRay module Scanners class Taskpaper < Scanner register_for :taskpaper file_extension 'taskpaper' protected def scan_tokens encoder, options until eos? if match = scan(/\S.*:.*$/) # project encoder.text_token(match, :namespace) elsif match = scan(/-.+@done.*/) # completed task encoder.text_token(match, :done) elsif match = scan(/-(?:[^@\n]+|@(?!due))*/) # task encoder.text_token(match, :plain) elsif match = scan(/@due.*/) # comment encoder.text_token(match, :important) elsif match = scan(/.+/) # comment encoder.text_token(match, :comment) elsif match = scan(/\s+/) # space encoder.text_token(match, :space) else # other encoder.text_token getch, :error end end encoder end end end end coderay-1.1.3/lib/coderay/scanners/text.rb000066400000000000000000000007141366440446100204750ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for plain text. # # Yields just one token of the kind :plain. # # Alias: +plaintext+, +plain+ class Text < Scanner register_for :text title 'Plain text' KINDS_NOT_LOC = [:plain] # :nodoc: protected def scan_tokens encoder, options encoder.text_token string, :plain encoder end end end end coderay-1.1.3/lib/coderay/scanners/xml.rb000066400000000000000000000003301366440446100203030ustar00rootroot00000000000000module CodeRay module Scanners load :html # Scanner for XML. # # Currently this is the same scanner as Scanners::HTML. class XML < HTML register_for :xml file_extension 'xml' end end end coderay-1.1.3/lib/coderay/scanners/yaml.rb000066400000000000000000000110161366440446100204500ustar00rootroot00000000000000module CodeRay module Scanners # Scanner for YAML. # # Based on the YAML scanner from Syntax by Jamis Buck. class YAML < Scanner register_for :yaml file_extension 'yml' KINDS_NOT_LOC = :all protected def scan_tokens encoder, options state = :initial key_indent = string_indent = 0 until eos? key_indent = nil if bol? if match = scan(/ +[\t ]*/) encoder.text_token match, :space elsif match = scan(/\n+/) encoder.text_token match, :space state = :initial if match.index(?\n) elsif match = scan(/#.*/) encoder.text_token match, :comment elsif bol? and case when match = scan(/---|\.\.\./) encoder.begin_group :head encoder.text_token match, :head encoder.end_group :head next when match = scan(/%.*/) encoder.text_token match, :doctype next end elsif state == :value and case when !check(/(?:"[^"]*")(?=: |:$)/) && match = scan(/"/) encoder.begin_group :string encoder.text_token match, :delimiter encoder.text_token match, :content if (match = scan(/ [^"\\]* (?: \\. [^"\\]* )* /mx)) && !match.empty? encoder.text_token match, :delimiter if match = scan(/"/) encoder.end_group :string next when match = scan(/[|>][-+]?/) encoder.begin_group :string encoder.text_token match, :delimiter string_indent = key_indent || column(pos - match.size) - 1 encoder.text_token matched, :content if scan(/(?:\n+ {#{string_indent + 1}}.*)+/) encoder.end_group :string next when match = scan(/(?![!"*&]).+?(?=$|\s+#)/) encoder.begin_group :string encoder.text_token match, :content string_indent = key_indent || column(pos - match.size) - 1 encoder.text_token matched, :content if scan(/(?:\n+ {#{string_indent + 1}}.*)+/) encoder.end_group :string next end elsif case when match = scan(/[-:](?= |$)/) state = :value if state == :colon && (match == ':' || match == '-') state = :value if state == :initial && match == '-' encoder.text_token match, :operator next when match = scan(/[,{}\[\]]/) encoder.text_token match, :operator next when state == :initial && match = scan(/[-\w.()\/ ]*\S(?= *:(?: |$))/) encoder.text_token match, :key key_indent = column(pos - match.size) - 1 state = :colon next when match = scan(/(?:"[^"\n]*"|'[^'\n]*')(?= *:(?: |$))/) encoder.begin_group :key encoder.text_token match[0,1], :delimiter encoder.text_token match[1..-2], :content if match.size > 2 encoder.text_token match[-1,1], :delimiter encoder.end_group :key key_indent = column(pos - match.size) - 1 state = :colon next when match = scan(/(![\w\/]+)(:([\w:]+))?/) encoder.text_token self[1], :type if self[2] encoder.text_token ':', :operator encoder.text_token self[3], :class end next when match = scan(/&\S+/) encoder.text_token match, :variable next when match = scan(/\*\w+/) encoder.text_token match, :global_variable next when match = scan(/< 'debug', # highlight for debugging (white on blue background) :annotation => 'annotation', # Groovy, Java :attribute_name => 'attribute-name', # HTML, CSS :attribute_value => 'attribute-value', # HTML :binary => 'binary', # Python, Ruby :char => 'char', # most scanners, also inside of strings :class => 'class', # lots of scanners, for different purposes also in CSS :class_variable => 'class-variable', # Ruby, YAML :color => 'color', # CSS :comment => 'comment', # most scanners :constant => 'constant', # PHP, Ruby :content => 'content', # inside of strings, most scanners :decorator => 'decorator', # Python :definition => 'definition', # CSS :delimiter => 'delimiter', # inside strings, comments and other types :directive => 'directive', # lots of scanners :doctype => 'doctype', # Goorvy, HTML, Ruby, YAML :docstring => 'docstring', # Python :done => 'done', # Taskpaper :entity => 'entity', # HTML :error => 'error', # invalid token, most scanners :escape => 'escape', # Ruby (string inline variables like #$foo, #@bar) :exception => 'exception', # Java, PHP, Python :filename => 'filename', # Diff :float => 'float', # most scanners :function => 'function', # CSS, JavaScript, PHP :global_variable => 'global-variable', # Ruby, YAML :hex => 'hex', # hexadecimal number; lots of scanners :id => 'id', # CSS :imaginary => 'imaginary', # Python :important => 'important', # CSS, Taskpaper :include => 'include', # C, Groovy, Java, Python, Sass :inline => 'inline', # nested code, eg. inline string evaluation; lots of scanners :inline_delimiter => 'inline-delimiter', # used instead of :inline > :delimiter FIXME: Why use inline_delimiter? :instance_variable => 'instance-variable', # Ruby :integer => 'integer', # most scanners :key => 'key', # lots of scanners, used together with :value :keyword => 'keyword', # reserved word that's actually implemented; most scanners :label => 'label', # C, PHP :local_variable => 'local-variable', # local and magic variables; some scanners :map => 'map', # Lua tables :modifier => 'modifier', # used inside on strings; lots of scanners :namespace => 'namespace', # Clojure, Java, Taskpaper :octal => 'octal', # lots of scanners :predefined => 'predefined', # predefined function: lots of scanners :predefined_constant => 'predefined-constant',# lots of scanners :predefined_type => 'predefined-type', # C, Java, PHP :preprocessor => 'preprocessor', # C, Delphi, HTML :pseudo_class => 'pseudo-class', # CSS :regexp => 'regexp', # Groovy, JavaScript, Ruby :reserved => 'reserved', # most scanners :shell => 'shell', # Ruby :string => 'string', # most scanners :symbol => 'symbol', # Clojure, Ruby, YAML :tag => 'tag', # CSS, HTML :type => 'type', # CSS, Java, SQL, YAML :value => 'value', # used together with :key; CSS, JSON, YAML :variable => 'variable', # Sass, SQL, YAML :change => 'change', # Diff :delete => 'delete', # Diff :head => 'head', # Diff, YAML :insert => 'insert', # Diff :eyecatcher => 'eyecatcher', # Diff :ident => false, # almost all scanners :operator => false, # almost all scanners :space => false, # almost all scanners :plain => false # almost all scanners ) TokenKinds[:method] = TokenKinds[:function] TokenKinds[:unknown] = TokenKinds[:plain] end coderay-1.1.3/lib/coderay/tokens.rb000066400000000000000000000124101366440446100171740ustar00rootroot00000000000000module CodeRay # The Tokens class represents a list of tokens returned from # a Scanner. It's actually just an Array with a few helper methods. # # A token itself is not a special object, just two elements in an Array: # * the _token_ _text_ (the original source of the token in a String) or # a _token_ _action_ (begin_group, end_group, begin_line, end_line) # * the _token_ _kind_ (a Symbol representing the type of the token) # # It looks like this: # # ..., '# It looks like this', :comment, ... # ..., '3.1415926', :float, ... # ..., '$^', :error, ... # # Some scanners also yield sub-tokens, represented by special # token actions, for example :begin_group and :end_group. # # The Ruby scanner, for example, splits "a string" into: # # [ # :begin_group, :string, # '"', :delimiter, # 'a string', :content, # '"', :delimiter, # :end_group, :string # ] # # Tokens can be used to save the output of a Scanners in a simple # Ruby object that can be send to an Encoder later: # # tokens = CodeRay.scan('price = 2.59', :ruby).tokens # tokens.encode(:html) # tokens.html # CodeRay.encoder(:html).encode_tokens(tokens) # # Tokens gives you the power to handle pre-scanned code very easily: # You can serialize it to a JSON string and store it in a database, pass it # around to encode it more than once, send it to other algorithms... class Tokens < Array # Remove Array#filter that is a new alias for Array#select on Ruby 2.6, # for method_missing called with filter method. undef_method :filter if instance_methods.include?(:filter) # The Scanner instance that created the tokens. attr_accessor :scanner # Encode the tokens using encoder. # # encoder can be # * a plugin name like :html oder 'statistic' # * an Encoder object # # options are passed to the encoder. def encode encoder, options = {} encoder = Encoders[encoder].new options if encoder.respond_to? :to_sym encoder.encode_tokens self, options end # Turn tokens into a string by concatenating them. def to_s encode CodeRay::Encoders::Encoder.new end # Redirects unknown methods to encoder calls. # # For example, if you call +tokens.html+, the HTML encoder # is used to highlight the tokens. def method_missing meth, options = {} encode meth, options rescue PluginHost::PluginNotFound super end # Split the tokens into parts of the given +sizes+. # # The result will be an Array of Tokens objects. The parts have # the text size specified by the parameter. In addition, each # part closes all opened tokens. This is useful to insert tokens # betweem them. # # This method is used by @Scanner#tokenize@ when called with an Array # of source strings. The Diff encoder uses it for inline highlighting. def split_into_parts *sizes return Array.new(sizes.size) { Tokens.new } if size == 2 && first == '' parts = [] opened = [] content = nil part = Tokens.new part_size = 0 size = sizes.first i = 0 for item in self case content when nil content = item when String if size && part_size + content.size > size # token must be cut if part_size < size # some part of the token goes into this part content = content.dup # content may no be safe to change part << content.slice!(0, size - part_size) << item end # close all open groups and lines... closing = opened.reverse.flatten.map do |content_or_kind| case content_or_kind when :begin_group :end_group when :begin_line :end_line else content_or_kind end end part.concat closing begin parts << part part = Tokens.new size = sizes[i += 1] end until size.nil? || size > 0 # ...and open them again. part.concat opened.flatten part_size = 0 redo unless content.empty? else part << content << item part_size += content.size end content = nil when Symbol case content when :begin_group, :begin_line opened << [content, item] when :end_group, :end_line opened.pop else raise ArgumentError, 'Unknown token action: %p, kind = %p' % [content, item] end part << content << item content = nil else raise ArgumentError, 'Token input junk: %p, kind = %p' % [content, item] end end parts << part parts << Tokens.new while parts.size < sizes.size parts end # Return the actual number of tokens. def count size / 2 end alias text_token push def begin_group kind; push :begin_group, kind end def end_group kind; push :end_group, kind end def begin_line kind; push :begin_line, kind end def end_line kind; push :end_line, kind end alias tokens concat end end coderay-1.1.3/lib/coderay/tokens_proxy.rb000066400000000000000000000027731366440446100204500ustar00rootroot00000000000000module CodeRay # The result of a scan operation is a TokensProxy, but should act like Tokens. # # This proxy makes it possible to use the classic CodeRay.scan.encode API # while still providing the benefits of direct streaming. class TokensProxy attr_accessor :input, :lang, :options, :block # Create a new TokensProxy with the arguments of CodeRay.scan. def initialize input, lang, options = {}, block = nil @input = input @lang = lang @options = options @block = block end # Call CodeRay.encode if +encoder+ is a Symbol; # otherwise, convert the receiver to tokens and call encoder.encode_tokens. def encode encoder, options = {} if encoder.respond_to? :to_sym CodeRay.encode(input, lang, encoder, options) else encoder.encode_tokens tokens, options end end # Tries to call encode; # delegates to tokens otherwise. def method_missing method, *args, &blk encode method.to_sym, *args rescue PluginHost::PluginNotFound tokens.send(method, *args, &blk) end # The (cached) result of the tokenized input; a Tokens instance. def tokens @tokens ||= scanner.tokenize(input) end # A (cached) scanner instance to use for the scan task. def scanner @scanner ||= CodeRay.scanner(lang, options, &block) end # Overwrite Struct#each. def each *args, &blk tokens.each(*args, &blk) self end end end coderay-1.1.3/lib/coderay/version.rb000066400000000000000000000000471366440446100173610ustar00rootroot00000000000000module CodeRay VERSION = '1.1.3' end coderay-1.1.3/rake_tasks/000077500000000000000000000000001366440446100153015ustar00rootroot00000000000000coderay-1.1.3/rake_tasks/benchmark.rake000066400000000000000000000001521366440446100200750ustar00rootroot00000000000000desc 'Do a benchmark' task :benchmark do ruby 'bench/bench.rb ruby html' end task :bench => :benchmark coderay-1.1.3/rake_tasks/code_statistics.rb000066400000000000000000000104101366440446100210060ustar00rootroot00000000000000# From rails (http://rubyonrails.com) # # Improved by murphy class CodeStatistics TEST_TYPES = /\btest/i # Create a new Code Statistic. # # Rakefile Example: # # desc 'Report code statistics (LOC) from the application' # task :stats => :copy_files do # require 'rake_helpers/code_statistics' # CodeStatistics.new( # ["Main", "lib"], # ["Tests", "test"], # ["Demos", "demo"] # ).to_s # end def initialize(*pairs) @pairs = pairs @statistics = calculate_statistics @total = if pairs.empty? then nil else calculate_total end end # Print a textual table viewing the stats # # Intended for console output. def print print_header @pairs.each { |name, path| print_line name, @statistics[name] } print_splitter if @total print_line 'Total', @total print_splitter end print_code_test_stats end private DEFAULT_FILE_PATTERN = /\.rb$/ def calculate_statistics @pairs.inject({}) do |stats, (name, path, pattern, is_ruby_code)| pattern ||= DEFAULT_FILE_PATTERN path = File.join path, '*.rb' stats[name] = calculate_directory_statistics path, pattern, is_ruby_code stats end end def calculate_directory_statistics directory, pattern = DEFAULT_FILE_PATTERN, is_ruby_code = true is_ruby_code = true if is_ruby_code.nil? stats = Hash.new 0 Dir[directory].each do |file_name| p "Scanning #{file_name}..." if $VERBOSE next unless file_name =~ pattern lines = codelines = classes = modules = methods = 0 empty_lines = comment_lines = 0 in_comment_block = false File.readlines(file_name).each do |line| lines += 1 if line[/^\s*$/] empty_lines += 1 elsif is_ruby_code case line when /^=end\b/ comment_lines += 1 in_comment_block = false when in_comment_block comment_lines += 1 when /^\s*class\b/ classes += 1 when /^\s*module\b/ modules += 1 when /^\s*def\b/ methods += 1 when /^\s*#/ comment_lines += 1 when /^=begin\b/ in_comment_block = false comment_lines += 1 when /^__END__$/ in_comment_block = true end end end codelines = lines - comment_lines - empty_lines stats[:lines] += lines stats[:comments] += comment_lines stats[:codelines] += codelines stats[:classes] += classes stats[:modules] += modules stats[:methods] += methods stats[:files] += 1 end stats end def calculate_total total = Hash.new 0 @statistics.each_value { |pair| pair.each { |k, v| total[k] += v } } total end def calculate_code code_loc = 0 @statistics.each { |k, v| code_loc += v[:codelines] unless k[TEST_TYPES] } code_loc end def calculate_tests test_loc = 0 @statistics.each { |k, v| test_loc += v[:codelines] if k[TEST_TYPES] } test_loc end def print_header print_splitter puts "| T=Test Name | Files | Lines | LOC | Comments | Classes | Modules | Methods | M/C | LOC/M |" print_splitter end def print_splitter puts "+---------------------------+-------+-------+-------+----------+---------+---------+---------+-----+-------+" end def print_line name, statistics m_over_c = (statistics[:methods] / (statistics[:classes] + statistics[:modules])) rescue m_over_c = 0 loc_over_m = (statistics[:codelines] / statistics[:methods]) - 2 rescue loc_over_m = 0 if name[TEST_TYPES] name = "T #{name}" else name = " #{name}" end line = "| %-25s | %5d | %5d | %5d | %8d | %7d | %7d | %7d | %3d | %5d |" % ( [name, *statistics.values_at(:files, :lines, :codelines, :comments, :classes, :modules, :methods)] + [m_over_c, loc_over_m] ) puts line end def print_code_test_stats code = calculate_code tests = calculate_tests puts " Code LOC = #{code} Test LOC = #{tests} Code:Test Ratio = [1 : #{sprintf("%.2f", tests.to_f / code)}]" puts "" end end # Run a test script. if $0 == __FILE__ $VERBOSE = true CodeStatistics.new( ['This dir', File.dirname(__FILE__)] ).print end coderay-1.1.3/rake_tasks/documentation.rake000066400000000000000000000010651366440446100210200ustar00rootroot00000000000000begin if RUBY_VERSION >= '1.8.7' gem 'rdoc' if defined? gem require 'rdoc/task' else require 'rake/rdoctask' end rescue LoadError warn 'Please gem install rdoc.' end desc 'Generate documentation for CodeRay' Rake::RDocTask.new :doc do |rd| rd.main = 'lib/README' rd.title = 'CodeRay Documentation' rd.options << '--line-numbers' << '--tab-width' << '2' rd.main = 'README_INDEX.rdoc' rd.rdoc_files.add 'README_INDEX.rdoc' rd.rdoc_files.add Dir['lib'] rd.rdoc_dir = 'doc' end if defined? Rake::RDocTask coderay-1.1.3/rake_tasks/generator.rake000066400000000000000000000054661366440446100201460ustar00rootroot00000000000000namespace :generate do desc 'generates a new scanner NAME=lang [ALT=alternative,plugin,ids] [EXT=file,extensions] [BASE=base lang]' task :scanner do raise 'I need a scanner name; use NAME=lang' unless scanner_class_name = ENV['NAME'] raise "Invalid lang: #{scanner_class_name}; use NAME=lang." unless /\A\w+\z/ === scanner_class_name require 'active_support/all' lang = scanner_class_name.underscore class_name = scanner_class_name.camelize def scanner_file_for_lang lang File.join(LIB_ROOT, 'coderay', 'scanners', lang + '.rb') end scanner_file = scanner_file_for_lang lang if File.exist? scanner_file print "#{scanner_file} already exists. Overwrite? [y|N] " exit unless $stdin.gets.chomp.downcase == 'y' end base_lang = ENV.fetch('BASE', 'json') base_scanner_file = scanner_file_for_lang(base_lang) puts "Reading base scanner #{base_scanner_file}..." base_scanner = File.read base_scanner_file puts "Writing new scanner #{scanner_file}..." File.open(scanner_file, 'w') do |file| file.write base_scanner. sub(/class \w+ < Scanner/, "class #{class_name} < Scanner"). sub('# Scanner for JSON (JavaScript Object Notation).', "# A scanner for #{scanner_class_name}."). sub(/register_for :\w+/, "register_for :#{lang}"). sub(/file_extension '\S+'/, "file_extension '#{ENV.fetch('EXT', lang).split(',').first}'") end test_dir = File.join(ROOT, 'test', 'scanners', lang) unless File.exist? test_dir puts "Creating test folder #{test_dir}..." sh "mkdir #{test_dir}" end test_suite_file = File.join(test_dir, 'suite.rb') unless File.exist? test_suite_file puts "Creating test suite file #{test_suite_file}..." base_suite = File.read File.join(test_dir, '..', 'ruby', 'suite.rb') File.open(test_suite_file, 'w') do |file| file.write base_suite.sub(/class Ruby/, "class #{class_name}") end end if extensions = ENV['EXT'] file_type_file = File.join(LIB_ROOT, 'coderay', 'helpers', 'filetype.rb') puts "Not automated. Remember to add your extensions to #{file_type_file}:" for ext in extensions.split(',') puts " '#{ext}' => :#{lang}," end end if alternative_ids = ENV['ALT'] && alternative_ids != lang map_file = File.join(LIB_ROOT, 'coderay', 'scanners', '_map.rb') puts "Not automated. Remember to add your alternative plugin ids to #{map_file}:" for id in alternative_ids.split(',') puts " :#{id} => :#{lang}," end end print 'Add to git? [Y|n] ' answer = $stdin.gets.chomp.downcase if answer.empty? || answer == 'y' sh "git add #{scanner_file}" cd File.join('test', 'scanners') do sh "git add #{lang}" end end end end coderay-1.1.3/rake_tasks/statistic.rake000066400000000000000000000013361366440446100201570ustar00rootroot00000000000000desc 'Report code statistics (LOC) from the application' task :stats do require './rake_tasks/code_statistics' CodeStatistics.new( ['Main', 'lib', /coderay.rb$/], ['CodeRay', 'lib/coderay/'], [' Scanners', 'lib/coderay/scanners/**'], [' Encoders', 'lib/coderay/encoders/**'], [' Helpers', 'lib/coderay/helpers/**'], [' Styles', 'lib/coderay/styles/**'], ['Executable', 'bin', /coderay$/], ['Executable Tests', 'test/executable/**'], ['Functional Tests', 'test/functional/**'], ['Scanner Tests', 'test/scanners/**', /suite\.rb$/], ['Unit Tests', 'test/unit/**'], # [' Test Data', 'test/scanners/**', /\.in\./, false], ['Demos', 'sample/**'] ).print end coderay-1.1.3/rake_tasks/test.rake000066400000000000000000000044671366440446100171370ustar00rootroot00000000000000namespace :test do desc 'run functional tests' task :functional do ruby './test/functional/suite.rb' ruby './test/functional/for_redcloth.rb' unless (''.chop! rescue true) end desc 'run unit tests' task :units do ruby './test/unit/suite.rb' end scanner_suite = 'test/scanners/suite.rb' desc 'run all scanner tests' task :scanners => :update_scanner_suite do ruby scanner_suite end desc 'update scanner test suite from GitHub' task :update_scanner_suite do if File.exist? scanner_suite Dir.chdir File.dirname(scanner_suite) do if File.directory? '.git' puts 'Updating scanner test suite...' sh 'git pull' elsif File.directory? '.svn' raise <<-ERROR Found the deprecated Subversion scanner test suite in ./#{File.dirname(scanner_suite)}. Please rename or remove it and run again to use the GitHub repository: mv test/scanners test/scanners-old ERROR else raise 'No scanner test suite found.' end end else puts 'Downloading scanner test suite...' sh 'git clone https://github.com/rubychan/coderay-scanner-tests.git test/scanners/' end unless ENV['SKIP_UPDATE_SCANNER_SUITE'] end namespace :scanner do Dir['./test/scanners/*'].each do |scanner| next unless File.directory? scanner lang = File.basename(scanner) desc "run all scanner tests for #{lang}" task lang => :update_scanner_suite do ruby "./test/scanners/suite.rb #{lang}" end end end desc 'clean test output files' task :clean do for file in Dir['test/scanners/**/*.actual.*'] rm file end for file in Dir['test/scanners/**/*.debug.diff'] rm file end for file in Dir['test/scanners/**/*.debug.diff.html'] rm file end for file in Dir['test/scanners/**/*.expected.html'] rm file end end desc 'test the CodeRay executable' task :exe do if RUBY_VERSION >= '1.8.7' ruby './test/executable/suite.rb' else puts puts "Can't run executable tests because shoulda-context requires Ruby 1.8.7+." puts "Skipping." end end end if RUBY_VERSION >= '1.9' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) end task :test => %w(test:functional test:units test:exe spec) coderay-1.1.3/spec/000077500000000000000000000000001366440446100141045ustar00rootroot00000000000000coderay-1.1.3/spec/coderay_spec.rb000066400000000000000000000015661366440446100171010ustar00rootroot00000000000000require File.expand_path('../spec_helper', __FILE__) RSpec.describe CodeRay do describe '::VERSION' do it "returns the Gem's version" do expect(CodeRay::VERSION).to match(/\A\d\.\d\.\d?\z/) end end describe '.coderay_path' do it 'returns an absolute file path to the given code file' do base = File.expand_path('../..', __FILE__) expect(CodeRay.coderay_path('file')).to eq("#{base}/lib/coderay/file") end end describe '.scan' do let(:code) { 'puts "Hello, World!"' } let(:tokens) do [ ['puts', :ident], [' ', :space], [:begin_group, :string], ['"', :delimiter], ['Hello, World!', :content], ['"', :delimiter], [:end_group, :string] ].flatten end it 'returns tokens' do expect(CodeRay.scan(code, :ruby).tokens).to eq(tokens) end end end coderay-1.1.3/spec/spec_helper.rb000066400000000000000000000116171366440446100167300ustar00rootroot00000000000000require 'simplecov' # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause # this file to always be loaded, without a need to explicitly require it in any # files. # # Given that it is always loaded, you are encouraged to keep this file as # light-weight as possible. Requiring heavyweight dependencies from this file # will add to the boot time of your test suite on EVERY test run, even for an # individual file that may not need all of that loaded. Instead, consider making # a separate helper file that requires the additional dependencies and performs # the additional setup, and require it from the spec files that actually need # it. # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest # assertions if you prefer. config.expect_with :rspec do |expectations| # This option will default to `true` in RSpec 4. It makes the `description` # and `failure_message` of custom matchers include text for helper methods # defined using `chain`, e.g.: # be_bigger_than(2).and_smaller_than(4).description # # => "be bigger than 2 and smaller than 4" # ...rather than: # # => "be bigger than 2" expectations.include_chain_clauses_in_custom_matcher_descriptions = true end if RUBY_VERSION >= '1.9' # rspec-mocks config goes here. You can use an alternate test double # library (such as bogus or mocha) by changing the `mock_with` option here. config.mock_with :rspec do |mocks| # Prevents you from mocking or stubbing a method that does not exist on # a real object. This is generally recommended, and will default to # `true` in RSpec 4. mocks.verify_partial_doubles = true end # This option will default to `:apply_to_host_groups` in RSpec 4 (and will # have no way to turn it off -- the option exists only for backwards # compatibility in RSpec 3). It causes shared context metadata to be # inherited by the metadata hash of host groups and examples, rather than # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups # The settings below are suggested to provide a good initial experience # with RSpec, but feel free to customize to your heart's content. =begin # This allows you to limit a spec run to individual examples or groups # you care about by tagging them with `:focus` metadata. When nothing # is tagged with `:focus`, all examples get run. RSpec also provides # aliases for `it`, `describe`, and `context` that include `:focus` # metadata: `fit`, `fdescribe` and `fcontext`, respectively. config.filter_run_when_matching :focus # Allows RSpec to persist some state between runs in order to support # the `--only-failures` and `--next-failure` CLI options. We recommend # you configure your source control system to ignore this file. config.example_status_persistence_file_path = "spec/examples.txt" # Limits the available syntax to the non-monkey patched syntax that is # recommended. For more details, see: # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode config.disable_monkey_patching! # This setting enables warnings. It's recommended, but in some cases may # be too noisy due to issues in dependencies. config.warnings = true # Many RSpec users commonly either run the entire suite or an individual # file, and it's useful to allow more verbose output when running an # individual spec file. if config.files_to_run.one? # Use the documentation formatter for detailed output, # unless a formatter has already been configured # (e.g. via a command-line flag). config.default_formatter = "doc" end # Print the 10 slowest examples and example groups at the # end of the spec run, to help surface which specs are running # particularly slow. config.profile_examples = 10 # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = :random # Seed global randomization in this process using the `--seed` CLI option. # Setting this allows you to use `--seed` to deterministically reproduce # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed =end end $:.unshift File.expand_path('../lib', __FILE__) require 'coderay' coderay-1.1.3/test/000077500000000000000000000000001366440446100141315ustar00rootroot00000000000000coderay-1.1.3/test/executable/000077500000000000000000000000001366440446100162525ustar00rootroot00000000000000coderay-1.1.3/test/executable/source.py000066400000000000000000000000271366440446100201230ustar00rootroot00000000000000class ClassName(): passcoderay-1.1.3/test/executable/source.rb000066400000000000000000000000241366440446100200730ustar00rootroot00000000000000class ClassName; endcoderay-1.1.3/test/executable/source_with_comments.rb000066400000000000000000000000361366440446100230360ustar00rootroot00000000000000# a class class ClassName end coderay-1.1.3/test/executable/suite.rb000066400000000000000000000146011366440446100177320ustar00rootroot00000000000000require 'simplecov' if RUBY_VERSION >= '1.9' require 'test/unit' require 'rubygems' unless defined? Gem require 'shoulda-context' require 'pathname' require 'json' $:.unshift File.expand_path('../../../lib', __FILE__) require 'coderay' puts "Running CodeRay #{CodeRay::VERSION} executable tests..." class TestCodeRayExecutable < Test::Unit::TestCase ROOT_DIR = Pathname.new(File.dirname(__FILE__)) + '..' + '..' EXECUTABLE = ROOT_DIR + 'bin' + 'coderay' RUBY_COMMAND = 'ruby' EXE_COMMAND = if RUBY_PLATFORM === 'java' && `ruby --ng -e '' 2> /dev/null` && $?.success? # use Nailgun "#{RUBY_COMMAND}--ng -I%s %s" else "#{RUBY_COMMAND} -I%s %s" end % [ROOT_DIR + 'lib', EXECUTABLE] def coderay args, options = {} if options[:fake_tty] command = "#{EXE_COMMAND} #{args} --tty" else command = "#{EXE_COMMAND} #{args}" end puts command if $DEBUG if options[:input] output = IO.popen "#{command} 2>&1", "r+" do |io| io.write options[:input] io.close_write io.read end else output = `#{command} 2>&1` end if output[EXECUTABLE.to_s] raise output else output end end context 'a simple call with no arguments' do should 'work' do assert_nothing_raised { coderay('') } end should 'print version and help' do assert_match(/CodeRay #{CodeRay::VERSION}/, coderay('')) assert_match(/usage:/, coderay('')) end end context 'version' do should 'be printed with -v' do assert_match(/\ACodeRay #{CodeRay::VERSION}\Z/, coderay('-v')) end should 'be printed with --version' do assert_match(/\ACodeRay #{CodeRay::VERSION}\Z/, coderay('--version')) end end context 'help' do should 'be printed with -h' do assert_match(/^usage:/, coderay('-h')) end should 'be printed with --help' do assert_match(/^usage:/, coderay('--help')) end should 'be printed with subcommand help' do assert_match(/^usage:/, coderay('help')) end end context 'commands' do should 'be printed with subcommand commands' do assert_match(/^ +help/, coderay('commands')) assert_match(/^ +version/, coderay('commands')) end end context 'highlighting a file to the terminal' do source_file = ROOT_DIR + 'test/executable/source.py' source = File.read source_file ansi_seq = /\e\[[0-9;]+m/ should 'not throw an error' do assert_nothing_raised { coderay(source_file, :fake_tty => true) } end should 'output its contents to stdout' do target = coderay(source_file, :fake_tty => true) assert_equal source, target.chomp.gsub(ansi_seq, '') end should 'output ANSI-colored text' do target = coderay(source_file, :fake_tty => true) assert_not_equal source, target.chomp assert_equal 6, target.scan(ansi_seq).size end end context 'highlighting a file into a pipe (source.rb -html > source.rb.html)' do source_file = ROOT_DIR + 'test/executable/source.rb' target_file = "#{source_file}.html" command = "#{source_file} -html > #{target_file}" source = File.read source_file pre = %r{
(.*?)
}m tag = /<[^>]*>/ should 'not throw an error' do assert_nothing_raised { coderay(command) } end should 'output its contents to the pipe' do coderay(command) target = File.read(target_file) if target = target[pre, 1] assert_equal source, target.gsub(tag, '').strip else flunk "target code has no
 tag: #{target}"
      end
    end
    should 'output valid HTML' do
      coderay(command)
      target = File.read(target_file)
      assert_not_equal source, target[pre, 1]
      assert_equal 6, target[pre, 1].scan(tag).size
      assert_match %r{\A\n\n}, target
    end
  end
  
  context 'highlighting a file into another file (source.rb source.rb.json)' do
    source_file = ROOT_DIR + 'test/executable/source.rb'
    target_file = "#{source_file}.json"
    command = "#{source_file} #{target_file}"
    
    source = File.read source_file
    
    text = /"text":"([^"]*)"/
    
    should 'not throw an error' do
      assert_nothing_raised { coderay(command) }
    end
    should 'output its contents to the file' do
      coderay(command)
      target = File.read(target_file)
      assert_equal source, target.scan(text).join
    end
    should 'output JSON' do
      coderay(command)
      target = File.read(target_file)
      assert_not_equal source, target
      assert_equal 6, target.scan(text).size
    end
  end
  
  context 'highlighting a file without explicit input type (source.py)' do
    source_file = ROOT_DIR + 'test/executable/source.py'
    command = "#{source_file} -html"
    
    source = File.read source_file
    
    pre = %r{
(.*?)
}m tag_class = /]*>/ should 'respect the file extension and highlight the input as Python' do target = coderay(command) assert_equal %w(keyword class keyword), target[pre, 1].scan(tag_class).flatten end end context 'highlighting a file with explicit input type (-ruby source.py)' do source_file = ROOT_DIR + 'test/executable/source.py' command = "-ruby #{source_file} -html" source = File.read source_file pre = %r{
(.*?)
}m tag_class = /]*>/ should 'ignore the file extension and highlight the input as Ruby' do target = coderay(command) assert_equal %w(keyword class), target[pre, 1].scan(tag_class).flatten end end context 'highlighting a file with explicit input and output type (-ruby source.py -span)' do source_file = ROOT_DIR + 'test/executable/source.py' command = "-ruby #{source_file} -span" source = File.read source_file span_tags = /<\/?span[^>]*>/ should 'just respect the output type and include span tags' do target = coderay(command) assert_equal source, target.chomp.gsub(span_tags, '') end end context 'the LOC counter' do source_file = ROOT_DIR + 'test/executable/source_with_comments.rb' command = "-ruby -loc" should 'work' do output = coderay(command, :input => <<-CODE) # test =begin =end test CODE assert_equal "1\n", output end end end coderay-1.1.3/test/functional/000077500000000000000000000000001366440446100162735ustar00rootroot00000000000000coderay-1.1.3/test/functional/basic.rb000066400000000000000000000211151366440446100177010ustar00rootroot00000000000000# encoding: utf-8 require 'test/unit' require File.expand_path('../../lib/assert_warning', __FILE__) $:.unshift File.expand_path('../../../lib', __FILE__) require 'coderay' class BasicTest < Test::Unit::TestCase def test_version assert_nothing_raised do assert_match(/\A\d\.\d\.\d?\z/, CodeRay::VERSION) end end def with_empty_load_path old_load_path = $:.dup $:.clear yield ensure $:.replace old_load_path end def test_autoload with_empty_load_path do assert_nothing_raised do CodeRay::Scanners::Java::BuiltinTypes end end end RUBY_TEST_CODE = 'puts "Hello, World!"' RUBY_TEST_TOKENS = [ ['puts', :ident], [' ', :space], [:begin_group, :string], ['"', :delimiter], ['Hello, World!', :content], ['"', :delimiter], [:end_group, :string] ].flatten def test_simple_scan assert_nothing_raised do assert_equal RUBY_TEST_TOKENS, CodeRay.scan(RUBY_TEST_CODE, :ruby).tokens end end RUBY_TEST_HTML = 'puts "' + 'Hello, World!"' def test_simple_highlight assert_nothing_raised do assert_equal RUBY_TEST_HTML, CodeRay.scan(RUBY_TEST_CODE, :ruby).html end end def test_scan_file CodeRay.scan_file __FILE__ end def test_encode assert_equal 1, CodeRay.encode('test', :python, :count) end def test_encode_tokens assert_equal 1, CodeRay.encode_tokens(CodeRay::Tokens['test', :string], :count) end def test_encode_file assert_equal File.read(__FILE__), CodeRay.encode_file(__FILE__, :text) end def test_highlight assert_match '
test
', CodeRay.highlight('test', :python) end def test_highlight_file assert_match "require 'test/unit'\n", CodeRay.highlight_file(__FILE__) end def test_duo assert_equal(RUBY_TEST_CODE, CodeRay::Duo[:plain, :text].highlight(RUBY_TEST_CODE)) assert_equal(RUBY_TEST_CODE, CodeRay::Duo[:plain => :text].highlight(RUBY_TEST_CODE)) end def test_duo_stream assert_equal(RUBY_TEST_CODE, CodeRay::Duo[:plain, :text].highlight(RUBY_TEST_CODE, :stream => true)) end def test_comment_filter assert_equal <<-EXPECTED, CodeRay.scan(<<-INPUT, :ruby).comment_filter.text #!/usr/bin/env ruby code more code EXPECTED #!/usr/bin/env ruby =begin A multi-line comment. =end code # A single-line comment. more code # and another comment, in-line. INPUT end def test_lines_of_code assert_equal 2, CodeRay.scan(<<-INPUT, :ruby).lines_of_code #!/usr/bin/env ruby =begin A multi-line comment. =end code # A single-line comment. more code # and another comment, in-line. INPUT rHTML = <<-RHTML <%= controller.controller_name.titleize %>: <%= controller.action_name %> <%= stylesheet_link_tag 'scaffold' %>

<%= flash[:notice] %>

<%= yield %>
RHTML assert_equal 0, CodeRay.scan(rHTML, :html).lines_of_code assert_equal 0, CodeRay.scan(rHTML, :php).lines_of_code assert_equal 0, CodeRay.scan(rHTML, :yaml).lines_of_code assert_equal 4, CodeRay.scan(rHTML, :erb).lines_of_code end def test_list_of_encoders assert_kind_of(Array, CodeRay::Encoders.list) assert CodeRay::Encoders.list.include?(:count) end def test_list_of_scanners assert_kind_of(Array, CodeRay::Scanners.list) assert CodeRay::Scanners.list.include?(:text) end def test_token_kinds assert_kind_of Hash, CodeRay::TokenKinds for kind, css_class in CodeRay::TokenKinds assert_kind_of Symbol, kind if css_class != false assert_kind_of String, css_class, "TokenKinds[%p] == %p" % [kind, css_class] end end assert_equal 'reserved', CodeRay::TokenKinds[:reserved] assert_equal false, CodeRay::TokenKinds[:shibboleet] end class Milk < CodeRay::Encoders::Encoder FILE_EXTENSION = 'cocoa' end class HoneyBee < CodeRay::Encoders::Encoder end def test_encoder_file_extension assert_nothing_raised do assert_equal 'html', CodeRay::Encoders::Page::FILE_EXTENSION assert_equal 'cocoa', Milk::FILE_EXTENSION assert_equal 'cocoa', Milk.new.file_extension assert_equal 'honeybee', HoneyBee::FILE_EXTENSION assert_equal 'honeybee', HoneyBee.new.file_extension end assert_raise NameError do HoneyBee::MISSING_CONSTANT end end def test_encoder_tokens encoder = CodeRay::Encoders::Encoder.new encoder.send :setup, {} assert_raise(ArgumentError) { encoder.token :strange, '' } encoder.token 'test', :debug end def test_encoder_deprecated_interface encoder = CodeRay::Encoders::Encoder.new encoder.send :setup, {} assert_warning 'Using old Tokens#<< interface.' do encoder << ['test', :content] end assert_raise ArgumentError do encoder << [:strange, :input] end assert_raise ArgumentError do encoder.encode_tokens [['test', :token]] end end def encoder_token_interface_deprecation_warning_given CodeRay::Encoders::Encoder.send :class_variable_get, :@@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN end def test_scanner_file_extension assert_equal 'rb', CodeRay::Scanners::Ruby.file_extension assert_equal 'rb', CodeRay::Scanners::Ruby.new.file_extension assert_equal 'java', CodeRay::Scanners::Java.file_extension assert_equal 'java', CodeRay::Scanners::Java.new.file_extension end def test_scanner_lang assert_equal :ruby, CodeRay::Scanners::Ruby.lang assert_equal :ruby, CodeRay::Scanners::Ruby.new.lang assert_equal :java, CodeRay::Scanners::Java.lang assert_equal :java, CodeRay::Scanners::Java.new.lang end def test_scanner_tokenize assert_equal ['foo', :plain], CodeRay::Scanners::Plain.new.tokenize('foo') assert_equal [['foo', :plain], ['bar', :plain]], CodeRay::Scanners::Plain.new.tokenize(['foo', 'bar']) CodeRay::Scanners::Plain.new.tokenize 42 end def test_scanner_tokens scanner = CodeRay::Scanners::Plain.new scanner.tokenize('foo') assert_equal ['foo', :plain], scanner.tokens scanner.string = '' assert_equal ['', :plain], scanner.tokens end def test_scanner_line_and_column scanner = CodeRay::Scanners::Plain.new "foo\nbär+quux" assert_equal 0, scanner.pos assert_equal 1, scanner.line assert_equal 1, scanner.column scanner.scan(/foo/) assert_equal 3, scanner.pos assert_equal 1, scanner.line assert_equal 4, scanner.column scanner.scan(/\n/) assert_equal 4, scanner.pos assert_equal 2, scanner.line assert_equal 1, scanner.column scanner.scan(/b/) assert_equal 5, scanner.pos assert_equal 2, scanner.line assert_equal 2, scanner.column scanner.scan(/a/) assert_equal 5, scanner.pos assert_equal 2, scanner.line assert_equal 2, scanner.column scanner.scan(/ä/) assert_equal 7, scanner.pos assert_equal 2, scanner.line assert_equal 4, scanner.column scanner.scan(/r/) assert_equal 8, scanner.pos assert_equal 2, scanner.line assert_equal 5, scanner.column end def test_scanner_use_subclasses assert_raise NotImplementedError do CodeRay::Scanners::Scanner.new end end class InvalidScanner < CodeRay::Scanners::Scanner end def test_scanner_scan_tokens assert_raise NotImplementedError do InvalidScanner.new.tokenize '' end end class RaisingScanner < CodeRay::Scanners::Scanner def scan_tokens encoder, options raise_inspect 'message', [], :initial end end def test_scanner_raise_inspect assert_raise CodeRay::Scanners::Scanner::ScanError do RaisingScanner.new.tokenize '' end end def test_scan_a_frozen_string assert_nothing_raised do CodeRay.scan RUBY_VERSION, :ruby CodeRay.scan RUBY_VERSION, :plain end end def test_scan_a_non_string assert_nothing_raised do CodeRay.scan 42, :ruby CodeRay.scan nil, :ruby CodeRay.scan self, :ruby CodeRay.encode ENV.to_hash, :ruby, :page CodeRay.highlight CodeRay, :plain end end end coderay-1.1.3/test/functional/examples.rb000066400000000000000000000122721366440446100204420ustar00rootroot00000000000000require 'test/unit' $:.unshift File.expand_path('../../../lib', __FILE__) require 'coderay' class ExamplesTest < Test::Unit::TestCase def test_examples # output as HTML div (using inline CSS styles) div = CodeRay.scan('puts "Hello, world!"', :ruby).div assert_equal <<-DIV, div
puts "Hello, world!"
DIV # ...with line numbers div = CodeRay.scan(<<-CODE.chomp, :ruby).div(:line_numbers => :table) 5.times do puts 'Hello, world!' end CODE assert_equal <<-DIV, div
1
2
3
5.times do
  puts 'Hello, world!'
end
DIV # output as standalone HTML page (using CSS classes) page = CodeRay.scan('puts "Hello, world!"', :ruby).page assert_match <<-PAGE, page
1
puts "Hello, world!"
PAGE # keep scanned tokens for later use tokens = CodeRay.scan('{ "just": "an", "example": 42 }', :json) assert_kind_of CodeRay::TokensProxy, tokens assert_equal ["{", :operator, " ", :space, :begin_group, :key, "\"", :delimiter, "just", :content, "\"", :delimiter, :end_group, :key, ":", :operator, " ", :space, :begin_group, :string, "\"", :delimiter, "an", :content, "\"", :delimiter, :end_group, :string, ",", :operator, " ", :space, :begin_group, :key, "\"", :delimiter, "example", :content, "\"", :delimiter, :end_group, :key, ":", :operator, " ", :space, "42", :integer, " ", :space, "}", :operator], tokens.tokens # produce a token statistic assert_equal <<-STATISTIC, tokens.statistic Code Statistics Tokens 26 Non-Whitespace 15 Bytes Total 31 Token Types (7): type count ratio size (average) ------------------------------------------------------------- TOTAL 26 100.00 % 1.2 delimiter 6 23.08 % 1.0 operator 5 19.23 % 1.0 space 5 19.23 % 1.0 key 4 15.38 % 0.0 :begin_group 3 11.54 % 0.0 :end_group 3 11.54 % 0.0 content 3 11.54 % 4.3 string 2 7.69 % 0.0 integer 1 3.85 % 2.0 STATISTIC # count the tokens assert_equal 26, tokens.count # produce a HTML div, but with CSS classes div = tokens.div(:css => :class) assert_equal <<-DIV, div
{ "just": "an", "example": 42 }
DIV # highlight a file (HTML div); guess the file type base on the extension assert_equal :ruby, CodeRay::FileType[__FILE__] # get a new scanner for Python python_scanner = CodeRay.scanner :python assert_kind_of CodeRay::Scanners::Python, python_scanner # get a new encoder for terminal terminal_encoder = CodeRay.encoder :term assert_kind_of CodeRay::Encoders::Terminal, terminal_encoder # scanning into tokens tokens = python_scanner.tokenize 'import this; # The Zen of Python' assert_equal ["import", :keyword, " ", :space, "this", :include, ";", :operator, " ", :space, "# The Zen of Python", :comment], tokens # format the tokens term = terminal_encoder.encode_tokens(tokens) assert_equal "\e[32mimport\e[0m \e[31mthis\e[0m; \e[1;30m# The Zen of Python\e[0m", term # re-using scanner and encoder ruby_highlighter = CodeRay::Duo[:ruby, :div] div = ruby_highlighter.encode('puts "Hello, world!"') assert_equal <<-DIV, div
puts "Hello, world!"
DIV end end coderay-1.1.3/test/functional/for_redcloth.rb000066400000000000000000000060741366440446100213010ustar00rootroot00000000000000require 'simplecov' if RUBY_VERSION >= '1.9' require 'test/unit' $:.unshift File.expand_path('../../../lib', __FILE__) require 'coderay' begin require 'rubygems' unless defined? Gem gem 'RedCloth', '>= 4.0.3' rescue nil require 'redcloth' rescue LoadError warn 'RedCloth not found - skipping for_redcloth tests.' undef RedCloth if defined? RedCloth end class BasicTest < Test::Unit::TestCase def test_for_redcloth require 'coderay/for_redcloth' assert_equal "

puts "Hello, World!"

", RedCloth.new('@[ruby]puts "Hello, World!"@').to_html assert_equal <<-BLOCKCODE.chomp,
puts "Hello, World!"
BLOCKCODE RedCloth.new('bc[ruby]. puts "Hello, World!"').to_html end def test_for_redcloth_no_lang require 'coderay/for_redcloth' assert_equal "

puts \"Hello, World!\"

", RedCloth.new('@puts "Hello, World!"@').to_html assert_equal <<-BLOCKCODE.chomp,
puts \"Hello, World!\"
BLOCKCODE RedCloth.new('bc. puts "Hello, World!"').to_html end def test_for_redcloth_style require 'coderay/for_redcloth' assert_equal <<-BLOCKCODE.chomp,
puts \"Hello, World!\"
BLOCKCODE RedCloth.new('bc{color: red}. puts "Hello, World!"').to_html end def test_for_redcloth_escapes require 'coderay/for_redcloth' assert_equal '

>

', RedCloth.new('@[ruby]>@').to_html assert_equal <<-BLOCKCODE.chomp,
&
BLOCKCODE RedCloth.new('bc[ruby]. &').to_html end def test_for_redcloth_escapes2 require 'coderay/for_redcloth' assert_equal "

#include <test.h>

", RedCloth.new('@[c]#include @').to_html end # See http://jgarber.lighthouseapp.com/projects/13054/tickets/124-code-markup-does-not-allow-brackets. def test_for_redcloth_false_positive require 'coderay/for_redcloth' assert_equal '

[project]_dff.skjd

', RedCloth.new('@[project]_dff.skjd@').to_html # false positive, but expected behavior / known issue assert_equal "

_dff.skjd

", RedCloth.new('@[ruby]_dff.skjd@').to_html assert_equal <<-BLOCKCODE.chomp, RedCloth.new('bc. [project]_dff.skjd').to_html
[project]_dff.skjd
BLOCKCODE end end if defined? RedClothcoderay-1.1.3/test/functional/suite.rb000066400000000000000000000007211366440446100177510ustar00rootroot00000000000000require 'simplecov' if RUBY_VERSION >= '1.9' require 'test/unit' $VERBOSE = $CODERAY_DEBUG = true $:.unshift File.expand_path('../../../lib', __FILE__) require 'coderay' mydir = File.dirname(__FILE__) suite = Dir[File.join(mydir, '*.rb')]. map { |tc| File.basename(tc).sub(/\.rb$/, '') } - %w'suite for_redcloth' puts "Running basic CodeRay #{CodeRay::VERSION} tests: #{suite.join(', ')}" for test_case in suite load File.join(mydir, test_case + '.rb') end coderay-1.1.3/test/lib/000077500000000000000000000000001366440446100146775ustar00rootroot00000000000000coderay-1.1.3/test/lib/README000066400000000000000000000001441366440446100155560ustar00rootroot00000000000000Contents: - test/unit: We need the old Test::Unit for the scanner test suite to work with Ruby 1.9. coderay-1.1.3/test/lib/assert_warning.rb000066400000000000000000000004611366440446100202530ustar00rootroot00000000000000class Test::Unit::TestCase def assert_warning expected_warning require 'stringio' oldstderr = $stderr $stderr = StringIO.new yield $stderr.rewind given_warning = $stderr.read.chomp assert_equal expected_warning, given_warning ensure $stderr = oldstderr end end coderay-1.1.3/test/lib/test/000077500000000000000000000000001366440446100156565ustar00rootroot00000000000000coderay-1.1.3/test/lib/test/unit.rb000066400000000000000000000255131366440446100171700ustar00rootroot00000000000000require 'test/unit/testcase' require 'test/unit/autorunner' module Test # :nodoc: # # = Test::Unit - Ruby Unit Testing Framework # # == Introduction # # Unit testing is making waves all over the place, largely due to the # fact that it is a core practice of XP. While XP is great, unit testing # has been around for a long time and has always been a good idea. One # of the keys to good unit testing, though, is not just writing tests, # but having tests. What's the difference? Well, if you just _write_ a # test and throw it away, you have no guarantee that something won't # change later which breaks your code. If, on the other hand, you _have_ # tests (obviously you have to write them first), and run them as often # as possible, you slowly build up a wall of things that cannot break # without you immediately knowing about it. This is when unit testing # hits its peak usefulness. # # Enter Test::Unit, a framework for unit testing in Ruby, helping you to # design, debug and evaluate your code by making it easy to write and # have tests for it. # # # == Notes # # Test::Unit has grown out of and superceded Lapidary. # # # == Feedback # # I like (and do my best to practice) XP, so I value early releases, # user feedback, and clean, simple, expressive code. There is always # room for improvement in everything I do, and Test::Unit is no # exception. Please, let me know what you think of Test::Unit as it # stands, and what you'd like to see expanded/changed/improved/etc. If # you find a bug, let me know ASAP; one good way to let me know what the # bug is is to submit a new test that catches it :-) Also, I'd love to # hear about any successes you have with Test::Unit, and any # documentation you might add will be greatly appreciated. My contact # info is below. # # # == Contact Information # # A lot of discussion happens about Ruby in general on the ruby-talk # mailing list (http://www.ruby-lang.org/en/ml.html), and you can ask # any questions you might have there. I monitor the list, as do many # other helpful Rubyists, and you're sure to get a quick answer. Of # course, you're also welcome to email me (Nathaniel Talbott) directly # at mailto:testunit@talbott.ws, and I'll do my best to help you out. # # # == Credits # # I'd like to thank... # # Matz, for a great language! # # Masaki Suketa, for his work on RubyUnit, which filled a vital need in # the Ruby world for a very long time. I'm also grateful for his help in # polishing Test::Unit and getting the RubyUnit compatibility layer # right. His graciousness in allowing Test::Unit to supercede RubyUnit # continues to be a challenge to me to be more willing to defer my own # rights. # # Ken McKinlay, for his interest and work on unit testing, and for his # willingness to dialog about it. He was also a great help in pointing # out some of the holes in the RubyUnit compatibility layer. # # Dave Thomas, for the original idea that led to the extremely simple # "require 'test/unit'", plus his code to improve it even more by # allowing the selection of tests from the command-line. Also, without # RDoc, the documentation for Test::Unit would stink a lot more than it # does now. # # Everyone who's helped out with bug reports, feature ideas, # encouragement to continue, etc. It's a real privilege to be a part of # the Ruby community. # # The guys at RoleModel Software, for putting up with me repeating, "But # this would be so much easier in Ruby!" whenever we're coding in Java. # # My Creator, for giving me life, and giving it more abundantly. # # # == License # # Test::Unit is copyright (c) 2000-2003 Nathaniel Talbott. It is free # software, and is distributed under the Ruby license. See the COPYING # file in the standard Ruby distribution for details. # # # == Warranty # # This software is provided "as is" and without any express or # implied warranties, including, without limitation, the implied # warranties of merchantibility and fitness for a particular # purpose. # # # == Author # # Nathaniel Talbott. # Copyright (c) 2000-2003, Nathaniel Talbott # # ---- # # = Usage # # The general idea behind unit testing is that you write a _test_ # _method_ that makes certain _assertions_ about your code, working # against a _test_ _fixture_. A bunch of these _test_ _methods_ are # bundled up into a _test_ _suite_ and can be run any time the # developer wants. The results of a run are gathered in a _test_ # _result_ and displayed to the user through some UI. So, lets break # this down and see how Test::Unit provides each of these necessary # pieces. # # # == Assertions # # These are the heart of the framework. Think of an assertion as a # statement of expected outcome, i.e. "I assert that x should be equal # to y". If, when the assertion is executed, it turns out to be # correct, nothing happens, and life is good. If, on the other hand, # your assertion turns out to be false, an error is propagated with # pertinent information so that you can go back and make your # assertion succeed, and, once again, life is good. For an explanation # of the current assertions, see Test::Unit::Assertions. # # # == Test Method & Test Fixture # # Obviously, these assertions have to be called within a context that # knows about them and can do something meaningful with their # pass/fail value. Also, it's handy to collect a bunch of related # tests, each test represented by a method, into a common test class # that knows how to run them. The tests will be in a separate class # from the code they're testing for a couple of reasons. First of all, # it allows your code to stay uncluttered with test code, making it # easier to maintain. Second, it allows the tests to be stripped out # for deployment, since they're really there for you, the developer, # and your users don't need them. Third, and most importantly, it # allows you to set up a common test fixture for your tests to run # against. # # What's a test fixture? Well, tests do not live in a vacuum; rather, # they're run against the code they are testing. Often, a collection # of tests will run against a common set of data, also called a # fixture. If they're all bundled into the same test class, they can # all share the setting up and tearing down of that data, eliminating # unnecessary duplication and making it much easier to add related # tests. # # Test::Unit::TestCase wraps up a collection of test methods together # and allows you to easily set up and tear down the same test fixture # for each test. This is done by overriding #setup and/or #teardown, # which will be called before and after each test method that is # run. The TestCase also knows how to collect the results of your # assertions into a Test::Unit::TestResult, which can then be reported # back to you... but I'm getting ahead of myself. To write a test, # follow these steps: # # * Make sure Test::Unit is in your library path. # * require 'test/unit' in your test script. # * Create a class that subclasses Test::Unit::TestCase. # * Add a method that begins with "test" to your class. # * Make assertions in your test method. # * Optionally define #setup and/or #teardown to set up and/or tear # down your common test fixture. # * You can now run your test as you would any other Ruby # script... try it and see! # # A really simple test might look like this (#setup and #teardown are # commented out to indicate that they are completely optional): # # require 'test/unit' # # class TC_MyTest < Test::Unit::TestCase # # def setup # # end # # # def teardown # # end # # def test_fail # assert(false, 'Assertion was false.') # end # end # # # == Test Runners # # So, now you have this great test class, but you still need a way to # run it and view any failures that occur during the run. This is # where Test::Unit::UI::Console::TestRunner (and others, such as # Test::Unit::UI::GTK::TestRunner) comes into play. The console test # runner is automatically invoked for you if you require 'test/unit' # and simply run the file. To use another runner, or to manually # invoke a runner, simply call its run class method and pass in an # object that responds to the suite message with a # Test::Unit::TestSuite. This can be as simple as passing in your # TestCase class (which has a class suite method). It might look # something like this: # # require 'test/unit/ui/console/testrunner' # Test::Unit::UI::Console::TestRunner.run(TC_MyTest) # # # == Test Suite # # As more and more unit tests accumulate for a given project, it # becomes a real drag running them one at a time, and it also # introduces the potential to overlook a failing test because you # forget to run it. Suddenly it becomes very handy that the # TestRunners can take any object that returns a Test::Unit::TestSuite # in response to a suite method. The TestSuite can, in turn, contain # other TestSuites or individual tests (typically created by a # TestCase). In other words, you can easily wrap up a group of # TestCases and TestSuites like this: # # require 'test/unit/testsuite' # require 'tc_myfirsttests' # require 'tc_moretestsbyme' # require 'ts_anothersetoftests' # # class TS_MyTests # def self.suite # suite = Test::Unit::TestSuite.new # suite << TC_MyFirstTests.suite # suite << TC_MoreTestsByMe.suite # suite << TS_AnotherSetOfTests.suite # return suite # end # end # Test::Unit::UI::Console::TestRunner.run(TS_MyTests) # # Now, this is a bit cumbersome, so Test::Unit does a little bit more # for you, by wrapping these up automatically when you require # 'test/unit'. What does this mean? It means you could write the above # test case like this instead: # # require 'test/unit' # require 'tc_myfirsttests' # require 'tc_moretestsbyme' # require 'ts_anothersetoftests' # # Test::Unit is smart enough to find all the test cases existing in # the ObjectSpace and wrap them up into a suite for you. It then runs # the dynamic suite using the console TestRunner. # # # == Questions? # # I'd really like to get feedback from all levels of Ruby # practitioners about typos, grammatical errors, unclear statements, # missing points, etc., in this document (or any other). # module Unit # If set to false Test::Unit will not automatically run at exit. def self.run=(flag) @run = flag end # Automatically run tests at exit? def self.run? @run ||= false end end end at_exit do unless $! || Test::Unit.run? exit Test::Unit::AutoRunner.run end end coderay-1.1.3/test/lib/test/unit/000077500000000000000000000000001366440446100166355ustar00rootroot00000000000000coderay-1.1.3/test/lib/test/unit/assertionfailederror.rb000066400000000000000000000004511366440446100234100ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. # License:: Ruby license. module Test module Unit # Thrown by Test::Unit::Assertions when an assertion fails. class AssertionFailedError < StandardError end end end coderay-1.1.3/test/lib/test/unit/assertions.rb000066400000000000000000000434631366440446100213660ustar00rootroot00000000000000# Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. # License:: Ruby license. require 'test/unit/assertionfailederror' require 'test/unit/util/backtracefilter' module Test module Unit ## # Test::Unit::Assertions contains the standard Test::Unit assertions. # Assertions is included in Test::Unit::TestCase. # # To include it in your own code and use its functionality, you simply # need to rescue Test::Unit::AssertionFailedError. Additionally you may # override add_assertion to get notified whenever an assertion is made. # # Notes: # * The message to each assertion, if given, will be propagated with the # failure. # * It is easy to add your own assertions based on assert_block(). # # = Example Custom Assertion # # def deny(boolean, message = nil) # message = build_message message, ' is not false or nil.', boolean # assert_block message do # not boolean # end # end module Assertions ## # The assertion upon which all other assertions are based. Passes if the # block yields true. # # Example: # assert_block "Couldn't do the thing" do # do_the_thing # end public def assert_block(message="assert_block failed.") # :yields: _wrap_assertion do if (! yield) raise AssertionFailedError.new(message.to_s) end end end ## # Asserts that +boolean+ is not false or nil. # # Example: # assert [1, 2].include?(5) public def assert(boolean, message=nil) _wrap_assertion do assert_block("assert should not be called with a block.") { !block_given? } assert_block(build_message(message, " is not true.", boolean)) { boolean } end end ## # Passes if +expected+ == +actual. # # Note that the ordering of arguments is important, since a helpful # error message is generated when this one fails that tells you the # values of expected and actual. # # Example: # assert_equal 'MY STRING', 'my string'.upcase public def assert_equal(expected, actual, message=nil) full_message = build_message(message, < expected but was . EOT assert_block(full_message) { expected == actual } end private def _check_exception_class(args) # :nodoc: args.partition do |klass| next if klass.instance_of?(Module) assert(Exception >= klass, "Should expect a class of exception, #{klass}") true end end private def _expected_exception?(actual_exception, exceptions, modules) # :nodoc: exceptions.include?(actual_exception.class) or modules.any? {|mod| actual_exception.is_a?(mod)} end ## # Passes if the block raises one of the given exceptions. # # Example: # assert_raise RuntimeError, LoadError do # raise 'Boom!!!' # end public def assert_raise(*args) _wrap_assertion do if Module === args.last message = "" else message = args.pop end exceptions, modules = _check_exception_class(args) expected = args.size == 1 ? args.first : args actual_exception = nil full_message = build_message(message, " exception expected but none was thrown.", expected) assert_block(full_message) do begin yield rescue Exception => actual_exception break end false end full_message = build_message(message, " exception expected but was\n?", expected, actual_exception) assert_block(full_message) {_expected_exception?(actual_exception, exceptions, modules)} actual_exception end end ## # Alias of assert_raise. # # Will be deprecated in 1.9, and removed in 2.0. public def assert_raises(*args, &block) assert_raise(*args, &block) end ## # Passes if +object+ .instance_of? +klass+ # # Example: # assert_instance_of String, 'foo' public def assert_instance_of(klass, object, message="") _wrap_assertion do assert_equal(Class, klass.class, "assert_instance_of takes a Class as its first argument") full_message = build_message(message, < expected to be an instance of but was . EOT assert_block(full_message){object.instance_of?(klass)} end end ## # Passes if +object+ is nil. # # Example: # assert_nil [1, 2].uniq! public def assert_nil(object, message="") assert_equal(nil, object, message) end ## # Passes if +object+ .kind_of? +klass+ # # Example: # assert_kind_of Object, 'foo' public def assert_kind_of(klass, object, message="") _wrap_assertion do assert(klass.kind_of?(Module), "The first parameter to assert_kind_of should be a kind_of Module.") full_message = build_message(message, "\nexpected to be kind_of\\?\n but was\n.", object, klass, object.class) assert_block(full_message){object.kind_of?(klass)} end end ## # Passes if +object+ .respond_to? +method+ # # Example: # assert_respond_to 'bugbear', :slice public def assert_respond_to(object, method, message="") _wrap_assertion do full_message = build_message(nil, "\ngiven as the method name argument to #assert_respond_to must be a Symbol or #respond_to\\?(:to_str).", method) assert_block(full_message) do method.kind_of?(Symbol) || method.respond_to?(:to_str) end full_message = build_message(message, < of type expected to respond_to\\?. EOT assert_block(full_message) { object.respond_to?(method) } end end ## # Passes if +string+ =~ +pattern+. # # Example: # assert_match(/\d+/, 'five, 6, seven') public def assert_match(pattern, string, message="") _wrap_assertion do pattern = case(pattern) when String Regexp.new(Regexp.escape(pattern)) else pattern end full_message = build_message(message, " expected to be =~\n.", string, pattern) assert_block(full_message) { string =~ pattern } end end ## # Passes if +actual+ .equal? +expected+ (i.e. they are the same # instance). # # Example: # o = Object.new # assert_same o, o public def assert_same(expected, actual, message="") full_message = build_message(message, < with id expected to be equal\\? to with id . EOT assert_block(full_message) { actual.equal?(expected) } end ## # Compares the +object1+ with +object2+ using +operator+. # # Passes if object1.__send__(operator, object2) is true. # # Example: # assert_operator 5, :>=, 4 public def assert_operator(object1, operator, object2, message="") _wrap_assertion do full_message = build_message(nil, "\ngiven as the operator for #assert_operator must be a Symbol or #respond_to\\?(:to_str).", operator) assert_block(full_message){operator.kind_of?(Symbol) || operator.respond_to?(:to_str)} full_message = build_message(message, < expected to be ? . EOT assert_block(full_message) { object1.__send__(operator, object2) } end end ## # Passes if block does not raise an exception. # # Example: # assert_nothing_raised do # [1, 2].uniq # end public def assert_nothing_raised(*args) _wrap_assertion do if Module === args.last message = "" else message = args.pop end exceptions, modules = _check_exception_class(args) begin yield rescue Exception => e if ((args.empty? && !e.instance_of?(AssertionFailedError)) || _expected_exception?(e, exceptions, modules)) assert_block(build_message(message, "Exception raised:\n?", e)){false} else raise end end nil end end ## # Flunk always fails. # # Example: # flunk 'Not done testing yet.' public def flunk(message="Flunked") assert_block(build_message(message)){false} end ## # Passes if ! +actual+ .equal? +expected+ # # Example: # assert_not_same Object.new, Object.new public def assert_not_same(expected, actual, message="") full_message = build_message(message, < with id expected to not be equal\\? to with id . EOT assert_block(full_message) { !actual.equal?(expected) } end ## # Passes if +expected+ != +actual+ # # Example: # assert_not_equal 'some string', 5 public def assert_not_equal(expected, actual, message="") full_message = build_message(message, " expected to be != to\n.", expected, actual) assert_block(full_message) { expected != actual } end ## # Passes if ! +object+ .nil? # # Example: # assert_not_nil '1 two 3'.sub!(/two/, '2') public def assert_not_nil(object, message="") full_message = build_message(message, " expected to not be nil.", object) assert_block(full_message){!object.nil?} end ## # Passes if +regexp+ !~ +string+ # # Example: # assert_no_match(/two/, 'one 2 three') public def assert_no_match(regexp, string, message="") _wrap_assertion do assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.") full_message = build_message(message, " expected to not match\n.", regexp, string) assert_block(full_message) { regexp !~ string } end end UncaughtThrow = {NameError => /^uncaught throw \`(.+)\'$/, ThreadError => /^uncaught throw \`(.+)\' in thread /} #` ## # Passes if the block throws +expected_symbol+ # # Example: # assert_throws :done do # throw :done # end public def assert_throws(expected_symbol, message="", &proc) _wrap_assertion do assert_instance_of(Symbol, expected_symbol, "assert_throws expects the symbol that should be thrown for its first argument") assert_block("Should have passed a block to assert_throws."){block_given?} caught = true begin catch(expected_symbol) do proc.call caught = false end full_message = build_message(message, " should have been thrown.", expected_symbol) assert_block(full_message){caught} rescue NameError, ThreadError => error if UncaughtThrow[error.class] !~ error.message raise error end full_message = build_message(message, " expected to be thrown but\n was thrown.", expected_symbol, $1.intern) flunk(full_message) end end end ## # Passes if block does not throw anything. # # Example: # assert_nothing_thrown do # [1, 2].uniq # end public def assert_nothing_thrown(message="", &proc) _wrap_assertion do assert(block_given?, "Should have passed a block to assert_nothing_thrown") begin proc.call rescue NameError, ThreadError => error if UncaughtThrow[error.class] !~ error.message raise error end full_message = build_message(message, " was thrown when nothing was expected", $1.intern) flunk(full_message) end assert(true, "Expected nothing to be thrown") end end ## # Passes if +expected_float+ and +actual_float+ are equal # within +delta+ tolerance. # # Example: # assert_in_delta 0.05, (50000.0 / 10**6), 0.00001 public def assert_in_delta(expected_float, actual_float, delta, message="") _wrap_assertion do {expected_float => "first float", actual_float => "second float", delta => "delta"}.each do |float, name| assert_respond_to(float, :to_f, "The arguments must respond to to_f; the #{name} did not") end assert_operator(delta, :>=, 0.0, "The delta should not be negative") full_message = build_message(message, < and expected to be within of each other. EOT assert_block(full_message) { (expected_float.to_f - actual_float.to_f).abs <= delta.to_f } end end ## # Passes if the method send returns a true value. # # +send_array+ is composed of: # * A receiver # * A method # * Arguments to the method # # Example: # assert_send [[1, 2], :include?, 4] public def assert_send(send_array, message="") _wrap_assertion do assert_instance_of(Array, send_array, "assert_send requires an array of send information") assert(send_array.size >= 2, "assert_send requires at least a receiver and a message name") full_message = build_message(message, < expected to respond to with a true value. EOT assert_block(full_message) { send_array[0].__send__(send_array[1], *send_array[2..-1]) } end end ## # Builds a failure message. +head+ is added before the +template+ and # +arguments+ replaces the '?'s positionally in the template. public def build_message(head, template=nil, *arguments) template &&= template.chomp return AssertionMessage.new(head, template, arguments) end private def _wrap_assertion @_assertion_wrapped ||= false unless (@_assertion_wrapped) @_assertion_wrapped = true begin add_assertion return yield ensure @_assertion_wrapped = false end else return yield end end ## # Called whenever an assertion is made. Define this in classes that # include Test::Unit::Assertions to record assertion counts. private def add_assertion end ## # Select whether or not to use the pretty-printer. If this option is set # to false before any assertions are made, pp.rb will not be required. public def self.use_pp=(value) AssertionMessage.use_pp = value end # :stopdoc: class AssertionMessage @use_pp = true class << self attr_accessor :use_pp end class Literal def initialize(value) @value = value end def inspect @value.to_s end end class Template def self.create(string) parts = (string ? string.scan(/(?=[^\\])\?|(?:\\\?|[^\?])+/m) : []) self.new(parts) end attr_reader :count def initialize(parts) @parts = parts @count = parts.find_all{|e| e == '?'}.size end def result(parameters) raise "The number of parameters does not match the number of substitutions." if(parameters.size != count) params = parameters.dup @parts.collect{|e| e == '?' ? params.shift : e.gsub(/\\\?/m, '?')}.join('') end end def self.literal(value) Literal.new(value) end include Util::BacktraceFilter def initialize(head, template_string, parameters) @head = head @template_string = template_string @parameters = parameters end def convert(object) case object when Exception < Message: <#{convert(object.message)}> ---Backtrace--- #{filter_backtrace(object.backtrace).join("\n")} --------------- EOM else if(self.class.use_pp) begin require 'pp' rescue LoadError self.class.use_pp = false return object.inspect end unless(defined?(PP)) PP.pp(object, '').chomp else object.inspect end end end def template @template ||= Template.create(@template_string) end def add_period(string) (string =~ /\.\Z/ ? string : string + '.') end def to_s message_parts = [] if (@head) head = @head.to_s unless(head.empty?) message_parts << add_period(head) end end tail = template.result(@parameters.collect{|e| convert(e)}) message_parts << tail unless(tail.empty?) message_parts.join("\n") end end # :startdoc: end end end coderay-1.1.3/test/lib/test/unit/autorunner.rb000066400000000000000000000151021366440446100213630ustar00rootroot00000000000000require 'test/unit/ui/testrunnerutilities' require 'optparse' module Test module Unit class AutoRunner def self.run(force_standalone=false, default_dir=nil, argv=ARGV, &block) r = new(force_standalone || standalone?, &block) r.base = default_dir r.process_args(argv) r.run end def self.standalone? return false unless("-e" == $0) ObjectSpace.each_object(Class) do |klass| return false if(klass < TestCase) end true end RUNNERS = { :console => proc do |r| require 'test/unit/ui/console/testrunner' Test::Unit::UI::Console::TestRunner end, :gtk => proc do |r| require 'test/unit/ui/gtk/testrunner' Test::Unit::UI::GTK::TestRunner end, :gtk2 => proc do |r| require 'test/unit/ui/gtk2/testrunner' Test::Unit::UI::GTK2::TestRunner end, :fox => proc do |r| require 'test/unit/ui/fox/testrunner' Test::Unit::UI::Fox::TestRunner end, :tk => proc do |r| require 'test/unit/ui/tk/testrunner' Test::Unit::UI::Tk::TestRunner end, } OUTPUT_LEVELS = [ [:silent, UI::SILENT], [:progress, UI::PROGRESS_ONLY], [:normal, UI::NORMAL], [:verbose, UI::VERBOSE], ] COLLECTORS = { :objectspace => proc do |r| require 'test/unit/collector/objectspace' c = Collector::ObjectSpace.new c.filter = r.filters c.collect($0.sub(/\.rb\Z/, '')) end, :dir => proc do |r| require 'test/unit/collector/dir' c = Collector::Dir.new c.filter = r.filters c.pattern.concat(r.pattern) if(r.pattern) c.exclude.concat(r.exclude) if(r.exclude) c.base = r.base $:.push(r.base) if r.base c.collect(*(r.to_run.empty? ? ['.'] : r.to_run)) end, } attr_reader :suite attr_accessor :output_level, :filters, :to_run, :pattern, :exclude, :base, :workdir attr_writer :runner, :collector def initialize(standalone) Unit.run = true @standalone = standalone @runner = RUNNERS[:console] @collector = COLLECTORS[(standalone ? :dir : :objectspace)] @filters = [] @to_run = [] @output_level = UI::NORMAL @workdir = nil yield(self) if(block_given?) end def process_args(args = ARGV) begin options.order!(args) {|arg| @to_run << arg} rescue OptionParser::ParseError => e puts e puts options $! = nil abort else @filters << proc{false} unless(@filters.empty?) end not @to_run.empty? end def options @options ||= OptionParser.new do |o| o.banner = "Test::Unit automatic runner." o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]" o.on o.on('-r', '--runner=RUNNER', RUNNERS, "Use the given RUNNER.", "(" + keyword_display(RUNNERS) + ")") do |r| @runner = r end if(@standalone) o.on('-b', '--basedir=DIR', "Base directory of test suites.") do |b| @base = b end o.on('-w', '--workdir=DIR', "Working directory to run tests.") do |w| @workdir = w end o.on('-a', '--add=TORUN', Array, "Add TORUN to the list of things to run;", "can be a file or a directory.") do |a| @to_run.concat(a) end @pattern = [] o.on('-p', '--pattern=PATTERN', Regexp, "Match files to collect against PATTERN.") do |e| @pattern << e end @exclude = [] o.on('-x', '--exclude=PATTERN', Regexp, "Ignore files to collect against PATTERN.") do |e| @exclude << e end end o.on('-n', '--name=NAME', String, "Runs tests matching NAME.", "(patterns may be used).") do |n| n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n) case n when Regexp @filters << proc{|t| n =~ t.method_name ? true : nil} else @filters << proc{|t| n == t.method_name ? true : nil} end end o.on('-t', '--testcase=TESTCASE', String, "Runs tests in TestCases matching TESTCASE.", "(patterns may be used).") do |n| n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n) case n when Regexp @filters << proc{|t| n =~ t.class.name ? true : nil} else @filters << proc{|t| n == t.class.name ? true : nil} end end o.on('-I', "--load-path=DIR[#{File::PATH_SEPARATOR}DIR...]", "Appends directory list to $LOAD_PATH.") do |dirs| $LOAD_PATH.concat(dirs.split(File::PATH_SEPARATOR)) end o.on('-v', '--verbose=[LEVEL]', OUTPUT_LEVELS, "Set the output level (default is verbose).", "(" + keyword_display(OUTPUT_LEVELS) + ")") do |l| @output_level = l || UI::VERBOSE end o.on('--', "Stop processing options so that the", "remaining options will be passed to the", "test."){o.terminate} o.on('-h', '--help', 'Display this help.'){puts o; exit} o.on_tail o.on_tail('Deprecated options:') o.on_tail('--console', 'Console runner (use --runner).') do warn("Deprecated option (--console).") @runner = RUNNERS[:console] end o.on_tail('--gtk', 'GTK runner (use --runner).') do warn("Deprecated option (--gtk).") @runner = RUNNERS[:gtk] end o.on_tail('--fox', 'Fox runner (use --runner).') do warn("Deprecated option (--fox).") @runner = RUNNERS[:fox] end o.on_tail end end def keyword_display(array) list = array.collect {|e, *| e.to_s} Array === array or list.sort! list.collect {|e| e.sub(/^(.)([A-Za-z]+)(?=\w*$)/, '\\1[\\2]')}.join(", ") end def run @suite = @collector[self] result = @runner[self] or return false Dir.chdir(@workdir) if @workdir result.run(@suite, @output_level).passed? end end end end coderay-1.1.3/test/lib/test/unit/collector.rb000066400000000000000000000015511366440446100211520ustar00rootroot00000000000000module Test module Unit module Collector def initialize @filters = [] end def filter=(filters) @filters = case(filters) when Proc [filters] when Array filters end end def add_suite(destination, suite) to_delete = suite.tests.find_all{|t| !include?(t)} to_delete.each{|t| suite.delete(t)} destination << suite unless(suite.size == 0) end def include?(test) return true if(@filters.empty?) @filters.each do |filter| result = filter[test] if(result.nil?) next elsif(!result) return false else return true end end true end def sort(suites) suites.sort_by{|s| s.name} end end end end coderay-1.1.3/test/lib/test/unit/collector/000077500000000000000000000000001366440446100206235ustar00rootroot00000000000000coderay-1.1.3/test/lib/test/unit/collector/dir.rb000066400000000000000000000061561366440446100217360ustar00rootroot00000000000000require 'test/unit/testsuite' require 'test/unit/collector' module Test module Unit module Collector class Dir include Collector attr_reader :pattern, :exclude attr_accessor :base def initialize(dir=::Dir, file=::File, object_space=::ObjectSpace, req=nil) super() @dir = dir @file = file @object_space = object_space @req = req @pattern = [/\btest_.*\.rb\Z/m] @exclude = [] end def collect(*from) basedir = @base $:.push(basedir) if basedir if(from.empty?) recursive_collect('.', find_test_cases) elsif(from.size == 1) recursive_collect(from.first, find_test_cases) else suites = [] from.each do |f| suite = recursive_collect(f, find_test_cases) suites << suite unless(suite.tests.empty?) end suite = TestSuite.new("[#{from.join(', ')}]") sort(suites).each{|s| suite << s} suite end ensure $:.delete_at($:.rindex(basedir)) if basedir end def find_test_cases(ignore=[]) cases = [] @object_space.each_object(Class) do |c| cases << c if(c < TestCase && !ignore.include?(c)) end ignore.concat(cases) cases end def recursive_collect(name, already_gathered) sub_suites = [] path = realdir(name) if @file.directory?(path) dir_name = name unless name == '.' @dir.entries(path).each do |e| next if(e == '.' || e == '..') e_name = dir_name ? @file.join(dir_name, e) : e if @file.directory?(realdir(e_name)) next if /\ACVS\z/ =~ e sub_suite = recursive_collect(e_name, already_gathered) sub_suites << sub_suite unless(sub_suite.empty?) else next if /~\z/ =~ e_name or /\A\.\#/ =~ e if @pattern and !@pattern.empty? next unless @pattern.any? {|pat| pat =~ e_name} end if @exclude and !@exclude.empty? next if @exclude.any? {|pat| pat =~ e_name} end collect_file(e_name, sub_suites, already_gathered) end end else collect_file(name, sub_suites, already_gathered) end suite = TestSuite.new(@file.basename(name)) sort(sub_suites).each{|s| suite << s} suite end def collect_file(name, suites, already_gathered) dir = @file.dirname(@file.expand_path(name, @base)) $:.unshift(dir) if(@req) @req.require(name) else require(name) end find_test_cases(already_gathered).each{|t| add_suite(suites, t.suite)} ensure $:.delete_at($:.rindex(dir)) if(dir) end def realdir(path) if @base @file.join(@base, path) else path end end end end end end coderay-1.1.3/test/lib/test/unit/error.rb000066400000000000000000000026321366440446100203160ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. # License:: Ruby license. require 'test/unit/util/backtracefilter' module Test module Unit # Encapsulates an error in a test. Created by # Test::Unit::TestCase when it rescues an exception thrown # during the processing of a test. class Error include Util::BacktraceFilter attr_reader(:test_name, :exception) SINGLE_CHARACTER = 'E' # Creates a new Error with the given test_name and # exception. def initialize(test_name, exception) @test_name = test_name @exception = exception end # Returns a single character representation of an error. def single_character_display SINGLE_CHARACTER end # Returns the message associated with the error. def message "#{@exception.class.name}: #{@exception.message}" end # Returns a brief version of the error description. def short_display "#@test_name: #{message.split("\n")[0]}" end # Returns a verbose version of the error description. def long_display backtrace = filter_backtrace(@exception.backtrace).join("\n ") "Error:\n#@test_name:\n#{message}\n #{backtrace}" end # Overridden to return long_display. def to_s long_display end end end end coderay-1.1.3/test/lib/test/unit/failure.rb000066400000000000000000000024461366440446100206170ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. # License:: Ruby license. module Test module Unit # Encapsulates a test failure. Created by Test::Unit::TestCase # when an assertion fails. class Failure attr_reader :test_name, :location, :message SINGLE_CHARACTER = 'F' # Creates a new Failure with the given location and # message. def initialize(test_name, location, message) @test_name = test_name @location = location @message = message end # Returns a single character representation of a failure. def single_character_display SINGLE_CHARACTER end # Returns a brief version of the error description. def short_display "#@test_name: #{@message.split("\n")[0]}" end # Returns a verbose version of the error description. def long_display location_display = if(location.size == 1) location[0].sub(/\A(.+:\d+).*/, ' [\\1]') else "\n [#{location.join("\n ")}]" end "Failure:\n#@test_name#{location_display}:\n#@message" end # Overridden to return long_display. def to_s long_display end end end end coderay-1.1.3/test/lib/test/unit/testcase.rb000066400000000000000000000107051366440446100210000ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. # License:: Ruby license. require 'test/unit/assertions' require 'test/unit/failure' require 'test/unit/error' require 'test/unit/testsuite' require 'test/unit/assertionfailederror' require 'test/unit/util/backtracefilter' module Test module Unit # Ties everything together. If you subclass and add your own # test methods, it takes care of making them into tests and # wrapping those tests into a suite. It also does the # nitty-gritty of actually running an individual test and # collecting its results into a Test::Unit::TestResult object. class TestCase include Assertions include Util::BacktraceFilter attr_reader :method_name STARTED = name + "::STARTED" FINISHED = name + "::FINISHED" ## # These exceptions are not caught by #run. PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt, SystemExit] # Creates a new instance of the fixture for running the # test represented by test_method_name. def initialize(test_method_name) unless(respond_to?(test_method_name) and (method(test_method_name).arity == 0 || method(test_method_name).arity == -1)) throw :invalid_test end @method_name = test_method_name @test_passed = true end # Rolls up all of the test* methods in the fixture into # one suite, creating a new instance of the fixture for # each method. def self.suite method_names = public_instance_methods(true) tests = method_names.delete_if {|method_name| method_name !~ /^test./} suite = TestSuite.new(name) tests.sort.each do |test| catch(:invalid_test) do suite << new(test) end end if (suite.empty?) catch(:invalid_test) do suite << new("default_test") end end return suite end # Runs the individual test method represented by this # instance of the fixture, collecting statistics, failures # and errors in result. def run(result) yield(STARTED, name) @_result = result begin setup __send__(@method_name) rescue AssertionFailedError => e add_failure(e.message, e.backtrace) rescue Exception raise if PASSTHROUGH_EXCEPTIONS.include? $!.class add_error($!) ensure begin teardown rescue AssertionFailedError => e add_failure(e.message, e.backtrace) rescue Exception raise if PASSTHROUGH_EXCEPTIONS.include? $!.class add_error($!) end end result.add_run yield(FINISHED, name) end # Called before every test method runs. Can be used # to set up fixture information. def setup end # Called after every test method runs. Can be used to tear # down fixture information. def teardown end def default_test flunk("No tests were specified") end # Returns whether this individual test passed or # not. Primarily for use in teardown so that artifacts # can be left behind if the test fails. def passed? return @test_passed end private :passed? def size 1 end def add_assertion @_result.add_assertion end private :add_assertion def add_failure(message, all_locations=caller()) @test_passed = false @_result.add_failure(Failure.new(name, filter_backtrace(all_locations), message)) end private :add_failure def add_error(exception) @test_passed = false @_result.add_error(Error.new(name, exception)) end private :add_error # Returns a human-readable name for the specific test that # this instance of TestCase represents. def name "#{@method_name}(#{self.class.name})" end # Overridden to return #name. def to_s name end # It's handy to be able to compare TestCase instances. def ==(other) return false unless(other.kind_of?(self.class)) return false unless(@method_name == other.method_name) self.class == other.class end end end end coderay-1.1.3/test/lib/test/unit/testresult.rb000066400000000000000000000040361366440446100214030ustar00rootroot00000000000000#-- # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. # License:: Ruby license. require 'test/unit/util/observable' module Test module Unit # Collects Test::Unit::Failure and Test::Unit::Error so that # they can be displayed to the user. To this end, observers # can be added to it, allowing the dynamic updating of, say, a # UI. class TestResult include Util::Observable CHANGED = "CHANGED" FAULT = "FAULT" attr_reader(:run_count, :assertion_count) # Constructs a new, empty TestResult. def initialize @run_count, @assertion_count = 0, 0 @failures, @errors = Array.new, Array.new end # Records a test run. def add_run @run_count += 1 notify_listeners(CHANGED, self) end # Records a Test::Unit::Failure. def add_failure(failure) @failures << failure notify_listeners(FAULT, failure) notify_listeners(CHANGED, self) end # Records a Test::Unit::Error. def add_error(error) @errors << error notify_listeners(FAULT, error) notify_listeners(CHANGED, self) end # Records an individual assertion. def add_assertion @assertion_count += 1 notify_listeners(CHANGED, self) end # Returns a string contain the recorded runs, assertions, # failures and errors in this TestResult. def to_s "#{run_count} tests, #{assertion_count} assertions, #{failure_count} failures, #{error_count} errors" end # Returns whether or not this TestResult represents # successful completion. def passed? return @failures.empty? && @errors.empty? end # Returns the number of failures this TestResult has # recorded. def failure_count return @failures.size end # Returns the number of errors this TestResult has # recorded. def error_count return @errors.size end end end end coderay-1.1.3/test/lib/test/unit/testsuite.rb000066400000000000000000000036451366440446100212230ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. # License:: Ruby license. module Test module Unit # A collection of tests which can be #run. # # Note: It is easy to confuse a TestSuite instance with # something that has a static suite method; I know because _I_ # have trouble keeping them straight. Think of something that # has a suite method as simply providing a way to get a # meaningful TestSuite instance. class TestSuite attr_reader :name, :tests STARTED = name + "::STARTED" FINISHED = name + "::FINISHED" # Creates a new TestSuite with the given name. def initialize(name="Unnamed TestSuite") @name = name @tests = [] end # Runs the tests and/or suites contained in this # TestSuite. def run(result, &progress_block) yield(STARTED, name) @tests.each do |test| test.run(result, &progress_block) end yield(FINISHED, name) end # Adds the test to the suite. def <<(test) @tests << test self end def delete(test) @tests.delete(test) end # Retuns the rolled up number of tests in this suite; # i.e. if the suite contains other suites, it counts the # tests within those suites, not the suites themselves. def size total_size = 0 @tests.each { |test| total_size += test.size } total_size end def empty? tests.empty? end # Overridden to return the name given the suite at # creation. def to_s @name end # It's handy to be able to compare TestSuite instances. def ==(other) return false unless(other.kind_of?(self.class)) return false unless(@name == other.name) @tests == other.tests end end end end coderay-1.1.3/test/lib/test/unit/ui/000077500000000000000000000000001366440446100172525ustar00rootroot00000000000000coderay-1.1.3/test/lib/test/unit/ui/console/000077500000000000000000000000001366440446100207145ustar00rootroot00000000000000coderay-1.1.3/test/lib/test/unit/ui/console/testrunner.rb000066400000000000000000000072141366440446100234560ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. # License:: Ruby license. require 'test/unit/ui/testrunnermediator' require 'test/unit/ui/testrunnerutilities' module Test module Unit module UI module Console # Runs a Test::Unit::TestSuite on the console. class TestRunner extend TestRunnerUtilities # Creates a new TestRunner for running the passed # suite. If quiet_mode is true, the output while # running is limited to progress dots, errors and # failures, and the final result. io specifies # where runner output should go to; defaults to # STDOUT. def initialize(suite, output_level=NORMAL, io=STDOUT) if (suite.respond_to?(:suite)) @suite = suite.suite else @suite = suite end @output_level = output_level @io = io @already_outputted = false @faults = [] end # Begins the test run. def start setup_mediator attach_to_mediator return start_mediator end private def setup_mediator @mediator = create_mediator(@suite) suite_name = @suite.to_s if ( @suite.kind_of?(Module) ) suite_name = @suite.name end output("Loaded suite #{suite_name}") end def create_mediator(suite) return TestRunnerMediator.new(suite) end def attach_to_mediator @mediator.add_listener(TestResult::FAULT, &method(:add_fault)) @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started)) @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished)) @mediator.add_listener(TestCase::STARTED, &method(:test_started)) @mediator.add_listener(TestCase::FINISHED, &method(:test_finished)) end def start_mediator return @mediator.run_suite end def add_fault(fault) @faults << fault output_single(fault.single_character_display, PROGRESS_ONLY) @already_outputted = true end def started(result) @result = result output("Started") end def finished(elapsed_time) nl output("Finished in #{elapsed_time} seconds.") @faults.each_with_index do |fault, index| nl output("%3d) %s" % [index + 1, fault.long_display]) end nl output(@result) end def test_started(name) output_single(name + ": ", VERBOSE) end def test_finished(name) output_single(".", PROGRESS_ONLY) unless (@already_outputted) nl(VERBOSE) @already_outputted = false end def nl(level=NORMAL) output("", level) end def output(something, level=NORMAL) @io.puts(something) if (output?(level)) @io.flush end def output_single(something, level=NORMAL) @io.write(something) if (output?(level)) @io.flush end def output?(level) level <= @output_level end end end end end end if __FILE__ == $0 Test::Unit::UI::Console::TestRunner.start_command_line_test end coderay-1.1.3/test/lib/test/unit/ui/testrunnermediator.rb000066400000000000000000000040601366440446100235350ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. # License:: Ruby license. require 'test/unit' require 'test/unit/util/observable' require 'test/unit/testresult' module Test module Unit module UI # Provides an interface to write any given UI against, # hopefully making it easy to write new UIs. class TestRunnerMediator RESET = name + "::RESET" STARTED = name + "::STARTED" FINISHED = name + "::FINISHED" include Util::Observable # Creates a new TestRunnerMediator initialized to run # the passed suite. def initialize(suite) @suite = suite end # Runs the suite the TestRunnerMediator was created # with. def run_suite Unit.run = true begin_time = Time.now notify_listeners(RESET, @suite.size) result = create_result notify_listeners(STARTED, result) result_listener = result.add_listener(TestResult::CHANGED) do |updated_result| notify_listeners(TestResult::CHANGED, updated_result) end fault_listener = result.add_listener(TestResult::FAULT) do |fault| notify_listeners(TestResult::FAULT, fault) end @suite.run(result) do |channel, value| notify_listeners(channel, value) end result.remove_listener(TestResult::FAULT, fault_listener) result.remove_listener(TestResult::CHANGED, result_listener) end_time = Time.now elapsed_time = end_time - begin_time notify_listeners(FINISHED, elapsed_time) #"Finished in #{elapsed_time} seconds.") return result end private # A factory method to create the result the mediator # should run with. Can be overridden by subclasses if # one wants to use a different result. def create_result return TestResult.new end end end end end coderay-1.1.3/test/lib/test/unit/ui/testrunnerutilities.rb000066400000000000000000000021771366440446100237530ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. # License:: Ruby license. module Test module Unit module UI SILENT = 0 PROGRESS_ONLY = 1 NORMAL = 2 VERBOSE = 3 # Provides some utilities common to most, if not all, # TestRunners. # #-- # # Perhaps there ought to be a TestRunner superclass? There # seems to be a decent amount of shared code between test # runners. module TestRunnerUtilities # Creates a new TestRunner and runs the suite. def run(suite, output_level=NORMAL) return new(suite, output_level).start end # Takes care of the ARGV parsing and suite # determination necessary for running one of the # TestRunners from the command line. def start_command_line_test if ARGV.empty? puts "You should supply the name of a test suite file to the runner" exit end require ARGV[0].gsub(/.+::/, '') new(eval(ARGV[0])).start end end end end end coderay-1.1.3/test/lib/test/unit/util/000077500000000000000000000000001366440446100176125ustar00rootroot00000000000000coderay-1.1.3/test/lib/test/unit/util/backtracefilter.rb000066400000000000000000000024351366440446100232700ustar00rootroot00000000000000module Test module Unit module Util module BacktraceFilter TESTUNIT_FILE_SEPARATORS = %r{[\\/:]} TESTUNIT_PREFIX = __FILE__.split(TESTUNIT_FILE_SEPARATORS)[0..-3] TESTUNIT_RB_FILE = /\.rb\Z/ def filter_backtrace(backtrace, prefix=nil) return ["No backtrace"] unless(backtrace) split_p = if(prefix) prefix.split(TESTUNIT_FILE_SEPARATORS) else TESTUNIT_PREFIX end match = proc do |e| split_e = e.split(TESTUNIT_FILE_SEPARATORS)[0, split_p.size] next false unless(split_e[0..-2] == split_p[0..-2]) split_e[-1].sub(TESTUNIT_RB_FILE, '') == split_p[-1] end return backtrace unless(backtrace.detect(&match)) found_prefix = false new_backtrace = backtrace.reverse.reject do |e| if(match[e]) found_prefix = true true elsif(found_prefix) false else true end end.reverse new_backtrace = (new_backtrace.empty? ? backtrace : new_backtrace) new_backtrace = new_backtrace.reject(&match) new_backtrace.empty? ? backtrace : new_backtrace end end end end end coderay-1.1.3/test/lib/test/unit/util/observable.rb000066400000000000000000000054631366440446100222730ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. # License:: Ruby license. require 'test/unit/util/procwrapper' module Test module Unit module Util # This is a utility class that allows anything mixing # it in to notify a set of listeners about interesting # events. module Observable # We use this for defaults since nil might mean something NOTHING = "NOTHING/#{__id__}" # Adds the passed proc as a listener on the # channel indicated by channel_name. listener_key # is used to remove the listener later; if none is # specified, the proc itself is used. # # Whatever is used as the listener_key is # returned, making it very easy to use the proc # itself as the listener_key: # # listener = add_listener("Channel") { ... } # remove_listener("Channel", listener) def add_listener(channel_name, listener_key=NOTHING, &listener) # :yields: value unless(block_given?) raise ArgumentError.new("No callback was passed as a listener") end key = listener_key if (listener_key == NOTHING) listener_key = listener key = ProcWrapper.new(listener) end channels[channel_name] ||= {} channels[channel_name][key] = listener return listener_key end # Removes the listener indicated by listener_key # from the channel indicated by # channel_name. Returns the registered proc, or # nil if none was found. def remove_listener(channel_name, listener_key) channel = channels[channel_name] return nil unless (channel) key = listener_key if (listener_key.instance_of?(Proc)) key = ProcWrapper.new(listener_key) end if (channel.has_key?(key)) return channel.delete(key) end return nil end # Calls all the procs registered on the channel # indicated by channel_name. If value is # specified, it is passed in to the procs, # otherwise they are called with no arguments. # #-- # # Perhaps this should be private? Would it ever # make sense for an external class to call this # method directly? def notify_listeners(channel_name, *arguments) channel = channels[channel_name] return 0 unless (channel) listeners = channel.values listeners.each { |listener| listener.call(*arguments) } return listeners.size end private def channels @channels ||= {} return @channels end end end end end coderay-1.1.3/test/lib/test/unit/util/procwrapper.rb000066400000000000000000000021251366440446100225030ustar00rootroot00000000000000#-- # # Author:: Nathaniel Talbott. # Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. # License:: Ruby license. module Test module Unit module Util # Allows the storage of a Proc passed through '&' in a # hash. # # Note: this may be inefficient, since the hash being # used is not necessarily very good. In Observable, # efficiency is not too important, since the hash is # only accessed when adding and removing listeners, # not when notifying. class ProcWrapper # Creates a new wrapper for a_proc. def initialize(a_proc) @a_proc = a_proc @hash = a_proc.inspect.sub(/^(#<#{a_proc.class}:)/){''}.sub(/(>)$/){''}.hex end def hash return @hash end def ==(other) case(other) when ProcWrapper return @a_proc == other.to_proc else return super end end alias :eql? :== def to_proc return @a_proc end end end end end coderay-1.1.3/test/unit/000077500000000000000000000000001366440446100151105ustar00rootroot00000000000000coderay-1.1.3/test/unit/comment_filter.rb000066400000000000000000000017241366440446100204500ustar00rootroot00000000000000require 'test/unit' require 'coderay' class CommentFilterTest < Test::Unit::TestCase def test_filtering_comments tokens = CodeRay.scan <<-RUBY, :ruby #!/usr/bin/env ruby # a minimal Ruby program puts "Hello world!" RUBY assert_equal <<-RUBY_FILTERED, tokens.comment_filter.text #!/usr/bin/env ruby puts "Hello world!" RUBY_FILTERED end def test_filtering_docstrings tokens = CodeRay.scan <<-PYTHON, :python ''' Assuming this is file mymodule.py then this string, being the first statement in the file will become the mymodule modules docstring when the file is imported ''' class Myclass(): """ The class's docstring """ def mymethod(self): '''The method's docstring''' def myfunction(): """The function's docstring""" PYTHON assert_equal <<-PYTHON_FILTERED.chomp, tokens.comment_filter.text class Myclass(): def mymethod(self): def myfunction(): PYTHON_FILTERED end endcoderay-1.1.3/test/unit/count.rb000066400000000000000000000004221366440446100165630ustar00rootroot00000000000000require 'test/unit' require 'coderay' class CountTest < Test::Unit::TestCase def test_count tokens = CodeRay.scan <<-RUBY.strip, :ruby #!/usr/bin/env ruby # a minimal Ruby program puts "Hello world!" RUBY assert_equal 11, tokens.encode(:count) end endcoderay-1.1.3/test/unit/debug.rb000066400000000000000000000035321366440446100165260ustar00rootroot00000000000000require 'test/unit' require 'coderay' class DebugEncoderTest < Test::Unit::TestCase def test_creation debug = nil assert_nothing_raised do debug = CodeRay.encoder :debug end assert CodeRay::Encoders::Debug < CodeRay::Encoders::Encoder assert_kind_of CodeRay::Encoders::Encoder, debug end TEST_INPUT = CodeRay::Tokens[ ['10', :integer], ['(\\)', :operator], [:begin_group, :string], ['test', :content], [:end_group, :string], [:begin_line, :head], ["\n", :space], ["\n \t", :space], [" \n", :space], ["[]", :method], [:end_line, :head], ].flatten TEST_OUTPUT = <<-'DEBUG'.chomp integer(10)operator((\\\))stringhead[ method([])] DEBUG def test_filtering_text_tokens assert_equal TEST_OUTPUT, CodeRay::Encoders::Debug.new.encode_tokens(TEST_INPUT) assert_equal TEST_OUTPUT, TEST_INPUT.debug end end class DebugScannerTest < Test::Unit::TestCase def test_creation assert CodeRay::Scanners::Debug < CodeRay::Scanners::Scanner debug = nil assert_nothing_raised do debug = CodeRay.scanner :debug end assert_kind_of CodeRay::Scanners::Scanner, debug end TEST_INPUT = <<-'DEBUG'.chomp integer(10)operator((\\\))stringtest[ method([])] DEBUG TEST_OUTPUT = CodeRay::Tokens[ ['10', :integer], ['(\\)', :operator], [:begin_group, :string], ['test', :content], [:end_group, :string], [:begin_line, :unknown], ["\n\n \t \n", :space], ["[]", :method], [:end_line, :unknown], ].flatten def test_filtering_text_tokens assert_equal TEST_OUTPUT, CodeRay::Scanners::Debug.new.tokenize(TEST_INPUT) assert_kind_of CodeRay::TokensProxy, CodeRay.scan(TEST_INPUT, :debug) assert_equal TEST_OUTPUT, CodeRay.scan(TEST_INPUT, :debug).tokens end end coderay-1.1.3/test/unit/duo.rb000066400000000000000000000016201366440446100162230ustar00rootroot00000000000000require 'test/unit' require 'yaml' require 'coderay' class DuoTest < Test::Unit::TestCase def test_two_arguments duo = CodeRay::Duo[:ruby, :html] assert_kind_of CodeRay::Scanners[:ruby], duo.scanner assert_kind_of CodeRay::Encoders[:html], duo.encoder end def test_two_hash duo = CodeRay::Duo[:ruby => :html] assert_kind_of CodeRay::Scanners[:ruby], duo.scanner assert_kind_of CodeRay::Encoders[:html], duo.encoder end def test_call duo = CodeRay::Duo[:python => :yml] yaml = [["def", :keyword], [" ", :space], ["test", :method], [":", :operator], [" ", :space], [:begin_group, :string], ["\"", :delimiter], ["pass", :content], ["\"", :delimiter], [:end_group, :string]] assert_equal yaml, YAML.load(duo.call('def test: "pass"')) end end coderay-1.1.3/test/unit/file_type.rb000066400000000000000000000065071366440446100174250ustar00rootroot00000000000000require 'test/unit' require File.expand_path('../../lib/assert_warning', __FILE__) require 'coderay/helpers/file_type' class FileTypeTests < Test::Unit::TestCase include CodeRay def test_fetch assert_raise FileType::UnknownFileType do FileType.fetch '' end assert_throws :not_found do FileType.fetch '.' do throw :not_found end end assert_equal :default, FileType.fetch('c', :default) end def test_block_supersedes_default_warning assert_warning 'Block supersedes default value argument; use either.' do FileType.fetch('c', :default) { } end end def test_ruby assert_equal :ruby, FileType[__FILE__] assert_equal :ruby, FileType['test.rb'] assert_equal :ruby, FileType['test.java.rb'] assert_equal :java, FileType['test.rb.java'] assert_equal :ruby, FileType['C:\\Program Files\\x\\y\\c\\test.rbw'] assert_equal :ruby, FileType['/usr/bin/something/Rakefile'] assert_equal :ruby, FileType['~/myapp/gem/Rantfile'] assert_equal :ruby, FileType['./lib/tasks\repository.rake'] assert_not_equal :ruby, FileType['test_rb'] assert_not_equal :ruby, FileType['Makefile'] assert_not_equal :ruby, FileType['set.rb/set'] assert_not_equal :ruby, FileType['~/projects/blabla/rb'] end def test_c assert_equal :c, FileType['test.c'] assert_equal :c, FileType['C:\\Program Files\\x\\y\\c\\test.h'] assert_not_equal :c, FileType['test_c'] assert_not_equal :c, FileType['Makefile'] assert_not_equal :c, FileType['set.h/set'] assert_not_equal :c, FileType['~/projects/blabla/c'] end def test_cpp assert_equal :cpp, FileType['test.c++'] assert_equal :cpp, FileType['test.cxx'] assert_equal :cpp, FileType['test.hh'] assert_equal :cpp, FileType['test.hpp'] assert_equal :cpp, FileType['test.cu'] assert_equal :cpp, FileType['test.C'] assert_not_equal :cpp, FileType['test.c'] assert_not_equal :cpp, FileType['test.h'] end def test_html assert_equal :html, FileType['test.htm'] assert_equal :html, FileType['test.xhtml'] assert_equal :html, FileType['test.html.xhtml'] assert_equal :erb, FileType['_form.rhtml'] assert_equal :erb, FileType['_form.html.erb'] end def test_yaml assert_equal :yaml, FileType['test.yml'] assert_equal :yaml, FileType['test.yaml'] assert_equal :yaml, FileType['my.html.yaml'] assert_not_equal :yaml, FileType['YAML'] end def test_pathname require 'pathname' pn = Pathname.new 'test.rb' assert_equal :ruby, FileType[pn] dir = Pathname.new '/etc/var/blubb' assert_equal :ruby, FileType[dir + pn] assert_equal :cpp, FileType[dir + 'test.cpp'] end def test_no_shebang dir = './test' if File.directory? dir Dir.chdir dir do assert_equal :c, FileType['test.c'] end end end def test_shebang_empty_file require 'tmpdir' tmpfile = File.join(Dir.tmpdir, 'bla') File.open(tmpfile, 'w') { } # touch assert_equal nil, FileType[tmpfile, true] end def test_shebang_no_file assert_equal nil, FileType['i do not exist', true] end def test_shebang require 'tmpdir' tmpfile = File.join(Dir.tmpdir, 'bla') File.open(tmpfile, 'w') { |f| f.puts '#!/usr/bin/env ruby' } assert_equal :ruby, FileType[tmpfile, true] end end coderay-1.1.3/test/unit/filter.rb000066400000000000000000000021361366440446100167240ustar00rootroot00000000000000require 'test/unit' require 'coderay' class FilterTest < Test::Unit::TestCase def test_creation filter = nil assert_nothing_raised do filter = CodeRay.encoder :filter end assert CodeRay::Encoders::Filter < CodeRay::Encoders::Encoder assert_kind_of CodeRay::Encoders::Encoder, filter end def test_filtering_text_tokens tokens = CodeRay::Tokens.new 10.times do |i| tokens.text_token i.to_s, :index end assert_equal tokens, CodeRay::Encoders::Filter.new.encode_tokens(tokens) assert_equal CodeRay::Tokens, tokens.filter.class assert_equal tokens, tokens.filter end def test_filtering_block_tokens tokens = CodeRay::Tokens.new 10.times do |i| tokens.begin_group :index tokens.text_token i.to_s, :content tokens.end_group :index tokens.begin_line :index tokens.text_token i.to_s, :content tokens.end_line :index end assert_equal tokens, CodeRay::Encoders::Filter.new.encode_tokens(tokens) assert_equal CodeRay::Tokens, tokens.filter.class assert_equal tokens, tokens.filter end end coderay-1.1.3/test/unit/html.rb000066400000000000000000000077361366440446100164160ustar00rootroot00000000000000require 'test/unit' require 'coderay' class HtmlTest < Test::Unit::TestCase def test_break_lines_option snippets = {} snippets[:ruby] = {} snippets[:ruby][:in] = <<-RUBY ruby_inside = <<-RUBY_INSIDE This is tricky, isn't it? RUBY_INSIDE RUBY snippets[:ruby][:expected_with_option_off] = <<-HTML_OPT_INDEPENDENT_LINES_OFF ruby_inside = <<-RUBY_INSIDE This is tricky, isn't it? RUBY_INSIDE HTML_OPT_INDEPENDENT_LINES_OFF snippets[:ruby][:expected_with_option_on] = <<-HTML_OPT_INDEPENDENT_LINES_ON ruby_inside = <<-RUBY_INSIDE This is tricky, isn't it? RUBY_INSIDE HTML_OPT_INDEPENDENT_LINES_ON snippets[:java] = {} snippets[:java][:in] = <<-JAVA import java.lang.*; /** * This is some multiline javadoc * used to test the */ public class Test { public static final String MESSAGE = "My message\ To the world"; static void main() { /* * Another multiline * comment */ System.out.println(MESSAGE); } } JAVA snippets[:java][:expected_with_option_off] = <<-HTML_OPT_INDEPENDENT_LINES_OFF import java.lang.*; /** * This is some multiline javadoc * used to test the */ public class Test { public static final String MESSAGE = "My message To the world"; static void main() { /* * Another multiline * comment */ System.out.println(MESSAGE); } } HTML_OPT_INDEPENDENT_LINES_OFF snippets[:java][:expected_with_option_on] = <<-HTML_OPT_INDEPENDENT_LINES_ON import java.lang.*; /** * This is some multiline javadoc * used to test the */ public class Test { public static final String MESSAGE = "My message To the world"; static void main() { /* * Another multiline * comment */ System.out.println(MESSAGE); } } HTML_OPT_INDEPENDENT_LINES_ON for lang, code in snippets tokens = CodeRay.scan code[:in], lang assert_equal code[:expected_with_option_off], tokens.html assert_equal code[:expected_with_option_off], tokens.html(:break_lines => false) assert_equal code[:expected_with_option_on], tokens.html(:break_lines => true) end end end coderay-1.1.3/test/unit/json_encoder.rb000066400000000000000000000016051366440446100201070ustar00rootroot00000000000000require 'test/unit' require 'coderay' class JSONEncoderTest < Test::Unit::TestCase def test_json_output old_load_paths = $:.dup begin $:.delete '.' $:.delete File.dirname(__FILE__) json = CodeRay.scan('puts "Hello world!"', :ruby).json assert_equal [ { "type" => "text", "text" => "puts", "kind" => "ident" }, { "type" => "text", "text" => " ", "kind" => "space" }, { "type" => "block", "action" => "open", "kind" => "string" }, { "type" => "text", "text" => "\"", "kind" => "delimiter" }, { "type" => "text", "text" => "Hello world!", "kind" => "content" }, { "type" => "text", "text" => "\"", "kind" => "delimiter" }, { "type" => "block", "action" => "close", "kind" => "string" }, ], JSON.load(json) ensure for path in old_load_paths - $: $: << path end end end endcoderay-1.1.3/test/unit/lines_of_code.rb000066400000000000000000000024661366440446100202350ustar00rootroot00000000000000require 'test/unit' require 'coderay' $VERBOSE = true require File.expand_path('../../lib/assert_warning', __FILE__) class LinesOfCodeTest < Test::Unit::TestCase def test_creation assert CodeRay::Encoders::LinesOfCode < CodeRay::Encoders::Encoder filter = nil assert_nothing_raised do filter = CodeRay.encoder :loc end assert_kind_of CodeRay::Encoders::LinesOfCode, filter assert_nothing_raised do filter = CodeRay.encoder :lines_of_code end assert_kind_of CodeRay::Encoders::LinesOfCode, filter end def test_lines_of_code tokens = CodeRay.scan <<-RUBY, :ruby #!/usr/bin/env ruby # a minimal Ruby program puts "Hello world!" RUBY assert_equal 1, CodeRay::Encoders::LinesOfCode.new.encode_tokens(tokens) assert_equal 1, tokens.lines_of_code assert_equal 1, tokens.loc end class ScannerMockup KINDS_NOT_LOC = [:space] end def test_filtering_block_tokens tokens = CodeRay::Tokens.new tokens.concat ["Hello\n", :world] tokens.concat ["\n", :space] tokens.concat ["Hello\n", :comment] assert_warning 'Tokens have no associated scanner, counting all nonempty lines.' do assert_equal 1, tokens.lines_of_code end tokens.scanner = ScannerMockup.new assert_equal 2, tokens.lines_of_code end endcoderay-1.1.3/test/unit/null.rb000066400000000000000000000003541366440446100164110ustar00rootroot00000000000000require 'test/unit' require 'coderay' class NullTest < Test::Unit::TestCase def test_null ruby = <<-RUBY puts "Hello world!" RUBY tokens = CodeRay.scan ruby, :ruby assert_equal '', tokens.encode(:null) end endcoderay-1.1.3/test/unit/plugin.rb000077500000000000000000000034171366440446100167430ustar00rootroot00000000000000require 'test/unit' require 'pathname' $:.unshift File.expand_path('../../../lib', __FILE__) require 'coderay' class PluginScannerTest < Test::Unit::TestCase module Plugins extend CodeRay::PluginHost plugin_path File.dirname(__FILE__), 'plugins' class Plugin extend CodeRay::Plugin plugin_host Plugins end end module PluginsWithDefault extend CodeRay::PluginHost plugin_path File.dirname(__FILE__), 'plugins_with_default' class Plugin extend CodeRay::Plugin plugin_host PluginsWithDefault end default :default_plugin end def test_load require Pathname.new(__FILE__).realpath.dirname + 'plugins' + 'user_defined' + 'user_plugin' assert_equal 'UserPlugin', Plugins.load(:user_plugin).name end def test_load_all assert_instance_of Symbol, Plugins.load_all.first assert_operator Plugins.all_plugins.first, :<, Plugins::Plugin assert_equal 'The Example', Plugins.all_plugins.map { |plugin| plugin.title }.sort.first end def test_default assert_nothing_raised do assert_operator PluginsWithDefault[:gargamel], :<, PluginsWithDefault::Plugin end assert_equal PluginsWithDefault::Default, PluginsWithDefault.default end def test_plugin_not_found assert_raise CodeRay::PluginHost::PluginNotFound do Plugins[:thestral] end assert_raise ArgumentError do Plugins[14] end assert_raise ArgumentError do Plugins['test/test'] end assert_raise CodeRay::PluginHost::PluginNotFound do PluginsWithDefault[:example_without_register_for] end end def test_autoload_constants assert_operator Plugins::Example, :<, Plugins::Plugin end def test_title assert_equal 'The Example', Plugins::Example.title end end coderay-1.1.3/test/unit/plugins/000077500000000000000000000000001366440446100165715ustar00rootroot00000000000000coderay-1.1.3/test/unit/plugins/example.rb000066400000000000000000000001531366440446100205500ustar00rootroot00000000000000class Example < PluginScannerTest::Plugins::Plugin register_for :example title 'The Example' end coderay-1.1.3/test/unit/plugins/user_defined/000077500000000000000000000000001366440446100212255ustar00rootroot00000000000000coderay-1.1.3/test/unit/plugins/user_defined/user_plugin.rb000066400000000000000000000001341366440446100241040ustar00rootroot00000000000000class UserPlugin < PluginScannerTest::Plugins::Plugin register_for :user_plugin end coderay-1.1.3/test/unit/plugins_with_default/000077500000000000000000000000001366440446100213305ustar00rootroot00000000000000coderay-1.1.3/test/unit/plugins_with_default/default_plugin.rb000066400000000000000000000001551366440446100246600ustar00rootroot00000000000000class DefaultPlugin < PluginScannerTest::PluginsWithDefault::Plugin register_for :default_plugin end coderay-1.1.3/test/unit/plugins_with_default/example_without_register_for.rb000066400000000000000000000001631366440446100276450ustar00rootroot00000000000000class ExampleWithoutRegisterFor < PluginScannerTest::PluginsWithDefault::Plugin register_for :wrong_id end coderay-1.1.3/test/unit/statistic.rb000066400000000000000000000032721366440446100174500ustar00rootroot00000000000000require 'test/unit' require 'coderay' class StatisticEncoderTest < Test::Unit::TestCase def test_creation assert CodeRay::Encoders::Statistic < CodeRay::Encoders::Encoder stats = nil assert_nothing_raised do stats = CodeRay.encoder :statistic end assert_kind_of CodeRay::Encoders::Encoder, stats end TEST_INPUT = CodeRay::Tokens[ ['10', :integer], ['(\\)', :operator], [:begin_group, :string], ['test', :content], [:end_group, :string], [:begin_line, :test], ["\n", :space], ["\n \t", :space], [" \n", :space], ["[]", :method], [:end_line, :test], ].flatten TEST_OUTPUT = <<-'DEBUG' Code Statistics Tokens 11 Non-Whitespace 4 Bytes Total 20 Token Types (7): type count ratio size (average) ------------------------------------------------------------- TOTAL 11 100.00 % 1.8 space 3 27.27 % 3.0 string 2 18.18 % 0.0 test 2 18.18 % 0.0 :begin_group 1 9.09 % 0.0 :begin_line 1 9.09 % 0.0 :end_group 1 9.09 % 0.0 :end_line 1 9.09 % 0.0 content 1 9.09 % 4.0 integer 1 9.09 % 2.0 method 1 9.09 % 2.0 operator 1 9.09 % 3.0 DEBUG def test_filtering_text_tokens assert_equal TEST_OUTPUT, CodeRay::Encoders::Statistic.new.encode_tokens(TEST_INPUT) assert_equal TEST_OUTPUT, TEST_INPUT.statistic end endcoderay-1.1.3/test/unit/suite.rb000077500000000000000000000007171366440446100165760ustar00rootroot00000000000000require 'simplecov' if RUBY_VERSION >= '1.9' require 'test/unit' require 'rubygems' $VERBOSE = $CODERAY_DEBUG = true $:.unshift 'lib' mydir = File.dirname(__FILE__) suite = Dir[File.join(mydir, '*.rb')]. map { |tc| File.basename(tc).sub(/\.rb$/, '') } - %w'suite vhdl' puts "Running CodeRay unit tests: #{suite.join(', ')}" helpers = %w(file_type word_list tokens) for test_case in helpers + (suite - helpers) load File.join(mydir, test_case + '.rb') end coderay-1.1.3/test/unit/text.rb000066400000000000000000000003571366440446100164260ustar00rootroot00000000000000require 'test/unit' require 'coderay' class TextTest < Test::Unit::TestCase def test_count ruby = <<-RUBY puts "Hello world!" RUBY tokens = CodeRay.scan ruby, :ruby assert_equal ruby, tokens.encode(:text) end endcoderay-1.1.3/test/unit/token_kind_filter.rb000066400000000000000000000041061366440446100211300ustar00rootroot00000000000000require 'test/unit' require 'coderay' class TokenKindFilterTest < Test::Unit::TestCase def test_creation assert CodeRay::Encoders::TokenKindFilter < CodeRay::Encoders::Encoder assert CodeRay::Encoders::TokenKindFilter < CodeRay::Encoders::Filter filter = nil assert_nothing_raised do filter = CodeRay.encoder :token_kind_filter end assert_instance_of CodeRay::Encoders::TokenKindFilter, filter end def test_filtering_text_tokens tokens = CodeRay::Tokens.new for i in 1..10 tokens.text_token i.to_s, :index tokens.text_token ' ', :space if i < 10 end assert_equal 10, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :exclude => :space).count assert_equal 10, tokens.token_kind_filter(:exclude => :space).count assert_equal 9, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :include => :space).count assert_equal 9, tokens.token_kind_filter(:include => :space).count assert_equal 0, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :exclude => :all).count assert_equal 0, tokens.token_kind_filter(:exclude => :all).count end def test_filtering_block_tokens tokens = CodeRay::Tokens.new 10.times do |i| tokens.begin_group :index tokens.text_token i.to_s, :content tokens.end_group :index tokens.begin_group :naught if i == 5 tokens.end_group :naught if i == 7 tokens.begin_line :blubb tokens.text_token i.to_s, :content tokens.end_line :blubb end assert_equal 16, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :include => :blubb).count assert_equal 16, tokens.token_kind_filter(:include => :blubb).count assert_equal 24, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :include => [:blubb, :content]).count assert_equal 24, tokens.token_kind_filter(:include => [:blubb, :content]).count assert_equal 32, CodeRay::Encoders::TokenKindFilter.new.encode_tokens(tokens, :exclude => :index).count assert_equal 32, tokens.token_kind_filter(:exclude => :index).count end end coderay-1.1.3/test/unit/tokens.rb000066400000000000000000000037711366440446100167500ustar00rootroot00000000000000require 'test/unit' require 'coderay' class TokensTest < Test::Unit::TestCase def test_creation assert CodeRay::Tokens < Array tokens = nil assert_nothing_raised do tokens = CodeRay::Tokens.new end assert_kind_of Array, tokens end def test_adding_tokens tokens = make_tokens assert_equal tokens.size, 8 assert_equal tokens.count, 4 end def test_to_s assert_equal 'string()', make_tokens.to_s end def test_encode_with_nonsense assert_raise NoMethodError do make_tokens.nonsense end end def test_split_into_parts parts_4_3 = [ ["stri", :type], ["ng", :type, :begin_group, :operator, "(", :content, :end_group, :operator], [:begin_group, :operator, ")", :content, :end_group, :operator] ] assert_equal parts_4_3, make_tokens.split_into_parts(4, 3) assert_equal [make_tokens.to_a], make_tokens.split_into_parts parts_7_0_1 = [ ["string", :type, :begin_group, :operator, "(", :content, :end_group, :operator], [], [:begin_group, :operator, ")", :content, :end_group, :operator] ] assert_equal parts_7_0_1, make_tokens.split_into_parts(7, 0, 1) line = CodeRay::Tokens[:begin_line, :head, '...', :plain] line_parts = [ [:begin_line, :head, ".", :plain, :end_line, :head], [:begin_line, :head, "..", :plain] ] assert_equal line_parts, line.split_into_parts(1) assert_raise ArgumentError do CodeRay::Tokens[:bullshit, :input].split_into_parts end assert_raise ArgumentError do CodeRay::Tokens[42, 43].split_into_parts end end def test_encode assert_match(/\A\[\{(?:"type":"text"|"text":"string"|"kind":"type"|,){5}\},/, make_tokens.encode(:json)) end def make_tokens tokens = CodeRay::Tokens.new assert_nothing_raised do tokens.text_token 'string', :type tokens.begin_group :operator tokens.text_token '()', :content tokens.end_group :operator end tokens end endcoderay-1.1.3/test/unit/word_list.rb000066400000000000000000000032271366440446100174470ustar00rootroot00000000000000require 'test/unit' require 'coderay/helpers/word_list' class WordListTest < Test::Unit::TestCase include CodeRay # define word arrays RESERVED_WORDS = %w[ asm break case continue default do else ... ] PREDEFINED_TYPES = %w[ int long short char void ... ] PREDEFINED_CONSTANTS = %w[ EOF NULL ... ] # make a WordList IDENT_KIND = WordList.new(:ident). add(RESERVED_WORDS, :reserved). add(PREDEFINED_TYPES, :predefined_type). add(PREDEFINED_CONSTANTS, :predefined_constant) def test_word_list_example assert_equal :predefined_type, IDENT_KIND['void'] # assert_equal :predefined_constant, IDENT_KIND['...'] # not specified end def test_word_list list = WordList.new(:ident).add(['foobar'], :reserved) assert_equal :reserved, list['foobar'] assert_equal :ident, list['FooBar'] assert_equal 1, list.size end def test_case_ignoring_word_list list = WordList::CaseIgnoring.new(:ident).add(['foobar'], :reserved) assert_equal :ident, list['foo'] assert_equal :reserved, list['foobar'] assert_equal :reserved, list['FooBar'] assert_equal 1, list.size list = WordList::CaseIgnoring.new(:ident).add(['FooBar'], :reserved) assert_equal :ident, list['foo'] assert_equal :reserved, list['foobar'] assert_equal :reserved, list['FooBar'] assert_equal 1, list.size end def test_dup list = WordList.new(:ident).add(['foobar'], :reserved) assert_equal :reserved, list['foobar'] list2 = list.dup list2.add(%w[foobar], :keyword) assert_equal :keyword, list2['foobar'] assert_equal :reserved, list['foobar'] end end