bbsexp 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012, David Kahn
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,28 @@
1
+ Library for making bbcode like markup for message boards.
2
+
3
+ Look at test.rb and below for example usage.
4
+
5
+
6
+ You can define your own elements and chain them together, but they can only be 1 character:
7
+
8
+ "[biu]bold italic underline[.]" => "<strong><em><u>bold italic underline</u></em></strong>
9
+
10
+ you can also close multiple tags at once:
11
+
12
+ "[b][i][u]bold italic underline[...]" => "<strong><em><u>bold italic underline</u></em></strong>"
13
+
14
+ unclosed tags are automatically closed at the end of the text:
15
+
16
+ "[b][i][u]bold italic underline" => "<strong><em><u>bold italic underline</u></em></strong>"
17
+
18
+ Redundant elements are automatically ignored:
19
+
20
+ "[bbiiuu][b][i][u][b][i][u]text" => "<strong><em><u>text</u></em></strong>"
21
+
22
+ The compiler follows rules such as not allowing block elements to be inside of inline elements:
23
+
24
+ "[i][q]q is blockquote" => "<em>q is blockquote</em>"
25
+
26
+ you can also define custom functions to operate on the tags:
27
+
28
+ "[r]reversed[.]" => "desrever"
@@ -0,0 +1,4 @@
1
+ require "strscan"
2
+ require_relative "bbsexp/dialect"
3
+ require_relative "bbsexp/expression"
4
+ require_relative "bbsexp/compiler"
@@ -0,0 +1,88 @@
1
+ module BBSexp
2
+ class Compiler
3
+ def initialize(parser, text)
4
+ @parser = parser
5
+ @text = text
6
+ @result = ''
7
+ @stack = []
8
+ @func_stack = []
9
+ @state = [3]
10
+ @locks = Hash[ @parser.exps.keys.map{|k| [k, false] } ]
11
+ end
12
+
13
+ def build
14
+ @result << tokens.map do |type, value|
15
+ case type
16
+ when :string then eval_string value
17
+ when :exp then eval_exp value
18
+ end
19
+ end.join
20
+
21
+ # close unclosed expressions
22
+ @result << @stack.reverse.map {|token|
23
+ token.reverse.map {|exp|
24
+ @parser.exps[exp].tags.last }.join }.join
25
+ end
26
+
27
+ def tokens
28
+ return to_enum(:tokens) unless block_given?
29
+ scanner = StringScanner.new(@text)
30
+
31
+ while string = scanner.scan_until(@parser.regexp)
32
+ exp = scanner.matched
33
+ yield [:string, string[0..-(exp.size + 1)]] unless string == exp
34
+ yield [:exp, exp[1..-2]]
35
+ end
36
+ yield [:string, scanner.rest] unless scanner.eos?
37
+ end
38
+
39
+ def eval_string(string)
40
+ # run callbacks on string
41
+ @func_stack.reduce(string) {|memo, func| func.(memo) }
42
+ end
43
+
44
+ def eval_exp(exp)
45
+ # dont parse if we are in the no parse zone
46
+ return eval_string(@parser.brackets(exp)) if @state.last == 0 and not exp.include? @parser.no_parse
47
+ # process expressions
48
+ unless exp[0] == @parser.end_exp
49
+ register(exp) #returns start tags
50
+ else
51
+ terminate(exp) #returns end tags
52
+ end
53
+ end
54
+
55
+ def register(token)
56
+ @stack << active_exps = # append valid expressions to the stack
57
+ token.chars.to_a.uniq.select do |exp| # ignore redundant and illegal expressions
58
+ @locks[exp] == false and @parser.exps[exp] <= @state.last
59
+ end.sort do |a,b|
60
+ @parser.exps[b] <=> @parser.exps[a] # sort nocode > block > inline
61
+ end.map do |exp|
62
+ @locks[exp] = true # lock this expression to prevent its use deeper in the stack
63
+ @state << @parser.exps[exp].state # set parser state to current element's level
64
+ exp # return to token.active_exps as a valid expression
65
+ end
66
+
67
+ @func_stack += active_exps.map {|exp| @parser.exps[exp].block }.compact
68
+
69
+ # return the start tags for the token
70
+ active_exps.map {|exp| @parser.exps[exp].tags.first }.join
71
+ end
72
+
73
+ def terminate(token)
74
+ return @parser.brackets(token) if @stack.empty? # don't do anything if the stack is empty
75
+
76
+ token.chars.map do |close|
77
+ if exps = @stack.pop
78
+ exps.map do |exp|
79
+ @locks[exp] = false # unlock this expression so it can be used again
80
+ @state.pop # return parser state to parent scope
81
+ @func_stack.pop if @parser.exps[exp].block # disable function
82
+ @parser.exps[exp].tags.last # return end tag
83
+ end.reverse.join
84
+ end
85
+ end.join
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,57 @@
1
+ module BBSexp
2
+ module Dialect
3
+ module ClassMethods
4
+ attr_accessor :exps, :brackets, :end_exp, :no_parse
5
+ attr_reader :regexp, :end_exp
6
+
7
+ def self.extended(by)
8
+ by.instance_exec do
9
+ @exps = {}
10
+ end
11
+ end
12
+
13
+ def initialize
14
+ @brackets ||= '[]'
15
+ @no_parse ||= '`'
16
+ @end_exp ||= '|'
17
+ exps = [ @brackets[0],
18
+ @exps.keys.join,
19
+ @end_exp,
20
+ @no_parse,
21
+ @brackets[1] ].map {|v| Regexp.escape(v) }
22
+
23
+ regexp = "%s([%s]+|%s+(%s)?)%s" % exps
24
+ @regexp = Regexp.new(regexp)
25
+ @initialized = true
26
+ end
27
+
28
+ def brackets(brackets=nil)
29
+ return @brackets[0] + brackets + @brackets[1] if @brackets
30
+ @brackets ||= brackets
31
+ end
32
+
33
+ def end_exp(exp=nil)
34
+ @end_exp ||= exp
35
+ end
36
+
37
+ def method_missing(level, sym, args={})
38
+ exp(sym, level, args)
39
+ end
40
+
41
+ def exp(sym, state, args={})
42
+ sym = sym.to_s
43
+ @no_parse = sym if state == :noparse
44
+ @exps[sym] = Expression.new(sym, state, args)
45
+ end
46
+
47
+ def parse(text)
48
+ initialize unless @initialized
49
+ Compiler.new(self, text).build
50
+ end
51
+ end
52
+
53
+ def self.included(by)
54
+ by.extend ClassMethods
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,23 @@
1
+ module BBSexp
2
+ class Expression
3
+ include Comparable
4
+ attr_reader :sym, :state, :tags, :block
5
+
6
+ def initialize(sym, state, args={})
7
+ @sym = sym
8
+ @state = case state
9
+ when :block then 3
10
+ when :inline then 2
11
+ when :func then 1
12
+ when :noparse then 0
13
+ else state
14
+ end
15
+ @tags = args[:tags] || ['', '']
16
+ @block = args[:func]
17
+ end
18
+
19
+ def <=>(b)
20
+ @state <=> (b.class == Expression ? b.state : b.to_i)
21
+ end
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bbsexp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - tca
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-04 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Concise BBcode Framework
15
+ email:
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE
21
+ - README.md
22
+ - lib/bbsexp.rb
23
+ - lib/bbsexp/expression.rb
24
+ - lib/bbsexp/compiler.rb
25
+ - lib/bbsexp/dialect.rb
26
+ homepage: https://github.com/tca/bbsexp
27
+ licenses: []
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.9.2
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 1.8.13
47
+ signing_key:
48
+ specification_version: 3
49
+ summary: Concise BBcode Framework
50
+ test_files: []