citrus 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/citrus.gemspec +3 -2
- data/examples/calc_sugar.rb +0 -1
- data/extras/citrus.vim +92 -0
- data/lib/citrus.rb +14 -16
- data/lib/citrus/{peg.rb → file.rb} +4 -6
- data/test/calc_peg_test.rb +5 -0
- data/test/calc_sugar_test.rb +5 -0
- data/test/calc_test.rb +5 -0
- data/test/{peg_test.rb → file_test.rb} +7 -9
- data/test/grammar_test.rb +3 -3
- data/test/match_test.rb +12 -0
- metadata +7 -6
data/citrus.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'citrus'
|
3
|
-
s.version = '1.
|
4
|
-
s.date = '2010-05-
|
3
|
+
s.version = '1.1.0'
|
4
|
+
s.date = '2010-05-18'
|
5
5
|
|
6
6
|
s.summary = 'Parsing Expressions for Ruby'
|
7
7
|
s.description = 'Parsing Expressions for Ruby'
|
@@ -13,6 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
|
14
14
|
s.files = Dir['lib/**/*.rb'] +
|
15
15
|
Dir['examples/**/*'] +
|
16
|
+
Dir['extras/**/*'] +
|
16
17
|
Dir['test/*.rb'] +
|
17
18
|
%w< citrus.gemspec Rakefile README >
|
18
19
|
|
data/examples/calc_sugar.rb
CHANGED
data/extras/citrus.vim
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
" Vim syntax file for Citrus grammars.
|
2
|
+
"
|
3
|
+
" Language: Citrus
|
4
|
+
" Author: Michael Jackson
|
5
|
+
|
6
|
+
if version < 600
|
7
|
+
syntax clear
|
8
|
+
elseif exists("b:current_syntax")
|
9
|
+
finish
|
10
|
+
endif
|
11
|
+
|
12
|
+
syn include @rubyTop syntax/ruby.vim
|
13
|
+
|
14
|
+
syn case match
|
15
|
+
|
16
|
+
syn match ctDoubleColon "::" contained
|
17
|
+
syn match ctConstant "\u\w*" contained
|
18
|
+
syn match ctVariable "\l\w*" contained
|
19
|
+
syn match ctModule "\(\(::\)\?\u\w*\)\+" contains=ctDoubleColon,ctConstant contained
|
20
|
+
|
21
|
+
" Comments
|
22
|
+
syn match ctComment "#.*" contains=@Spell
|
23
|
+
|
24
|
+
" Keywords
|
25
|
+
syn match ctKeyword "\<super\>" contained
|
26
|
+
|
27
|
+
" Terminals
|
28
|
+
syn region ctString matchgroup=ctStringDelimiter start="\"" end="\"" skip="\\\\\|\\\"" contains=@ctStringSpecial
|
29
|
+
syn region ctString matchgroup=ctStringDelimiter start="'" end="'" skip="\\\\\|\\'"
|
30
|
+
syn region ctRegexp matchgroup=ctRegexpDelimiter start="/" end="/[iomxneus]*" skip="\\\\\|\\/" contains=@ctRegexpSpecial contained display
|
31
|
+
syn region ctCharClass matchgroup=ctRegexpDelimiter start="\[" end="\]" skip="\\\\\|\\\[" contains=@ctRegexpSpecial contained display
|
32
|
+
syn match ctAnything "\." contained display
|
33
|
+
|
34
|
+
syn cluster ctStringSpecial contains=rubyStringEscape
|
35
|
+
syn cluster ctRegexpSpecial contains=rubyStringEscape,rubyRegexpSpecial,rubyRegexpEscape,rubyRegexpBrackets,rubyRegexpCharClass,rubyRegexpDot,rubyRegexpQuantifier,rubyRegexpAnchor,rubyRegexpParens,rubyRegexpComment
|
36
|
+
|
37
|
+
" Quantifiers
|
38
|
+
syn match ctQuantifier "+" contained display
|
39
|
+
syn match ctQuantifier "?" contained display
|
40
|
+
syn match ctQuantifier "\d*\*\d*" contained display
|
41
|
+
|
42
|
+
" Operators
|
43
|
+
syn match ctOperator "|" contained
|
44
|
+
syn match ctOperator "\l\w*:"me=e-1 contained
|
45
|
+
|
46
|
+
" Extensions
|
47
|
+
syn region ctRubyBlock start="{"ms=e+1 end="}"me=s-1 contains=@rubyTop contained
|
48
|
+
syn match ctTag "<\s*\(\(::\)\?\u\w*\)\+\s*>" contains=ctModule contained
|
49
|
+
|
50
|
+
" Declarations
|
51
|
+
syn match ctRequire "\<require\>" nextgroup=ctString skipwhite skipnl
|
52
|
+
syn match ctGrammar "\<grammar\>" nextgroup=ctModule skipwhite skipnl
|
53
|
+
syn match ctInclude "\<include\>" nextgroup=ctModule skipwhite skipnl contained
|
54
|
+
syn match ctRoot "\<root\>" nextgroup=ctVariable skipwhite skipnl contained
|
55
|
+
syn match ctRule "\<rule\>" nextgroup=ctVariable skipwhite skipnl contained
|
56
|
+
|
57
|
+
" Blocks
|
58
|
+
syn region ctGrammarBlock start="\<grammar\>" matchgroup=ctGrammar end="\<end\>" contains=ctComment,ctGrammar,ctInclude,ctRoot,ctRuleBlock fold
|
59
|
+
syn region ctRuleBlock start="\<rule\>" matchgroup=ctRule end="\<end\>" contains=ALLBUT,ctRequire,ctGrammar,ctInclude,ctRoot,ctConstant,ctVariable fold
|
60
|
+
|
61
|
+
" Groups
|
62
|
+
hi def link ctComment Comment
|
63
|
+
|
64
|
+
hi def link ctRequire Include
|
65
|
+
hi def link ctInclude Include
|
66
|
+
hi def link ctGrammar ctDefine
|
67
|
+
hi def link ctRoot ctDefine
|
68
|
+
hi def link ctRule ctDefine
|
69
|
+
hi def link ctDefine Define
|
70
|
+
|
71
|
+
hi def link ctConstant Type
|
72
|
+
hi def link ctVariable Function
|
73
|
+
hi def link ctKeyword Keyword
|
74
|
+
|
75
|
+
hi def link ctString ctTerminal
|
76
|
+
hi def link ctRegexp ctTerminal
|
77
|
+
hi def link ctCharClass ctTerminal
|
78
|
+
hi def link ctAnything ctTerminal
|
79
|
+
hi def link ctTerminal String
|
80
|
+
|
81
|
+
hi def link ctRegexpDelimiter ctStringDelimiter
|
82
|
+
hi def link ctStringDelimiter Delimiter
|
83
|
+
|
84
|
+
hi def link ctRegexpSpecial ctStringSpecial
|
85
|
+
hi def link ctStringSpecial Special
|
86
|
+
|
87
|
+
hi def link ctQuantifier ctOperator
|
88
|
+
hi def link ctOperator Operator
|
89
|
+
|
90
|
+
let b:current_syntax = "citrus"
|
91
|
+
|
92
|
+
" vim: nowrap:
|
data/lib/citrus.rb
CHANGED
@@ -4,29 +4,31 @@
|
|
4
4
|
#
|
5
5
|
# http://github.com/mjijackson/citrus
|
6
6
|
module Citrus
|
7
|
-
VERSION = [1,
|
7
|
+
VERSION = [1, 1, 0]
|
8
8
|
|
9
9
|
Infinity = 1.0 / 0
|
10
10
|
|
11
|
-
autoload '
|
11
|
+
autoload 'File', 'citrus/file'
|
12
12
|
|
13
13
|
# Returns the current version of Citrus as a string.
|
14
14
|
def self.version
|
15
15
|
VERSION.join('.')
|
16
16
|
end
|
17
17
|
|
18
|
+
F = ::File
|
19
|
+
|
18
20
|
# Loads the grammar from the given +file+ into the global scope using #eval.
|
19
21
|
def self.load(file)
|
20
|
-
file << '.citrus' unless
|
21
|
-
raise "Cannot find file #{file}" unless
|
22
|
-
raise "Cannot read file #{file}" unless
|
23
|
-
self.eval(
|
22
|
+
file << '.citrus' unless F.file?(file)
|
23
|
+
raise "Cannot find file #{file}" unless F.file?(file)
|
24
|
+
raise "Cannot read file #{file}" unless F.readable?(file)
|
25
|
+
self.eval(F.read(file))
|
24
26
|
end
|
25
27
|
|
26
28
|
# Evaluates the given Citrus parsing expression grammar +code+ in the global
|
27
29
|
# scope. Returns an array of any grammar modules that were created.
|
28
30
|
def self.eval(code)
|
29
|
-
file =
|
31
|
+
file = File.parse(code)
|
30
32
|
file.value
|
31
33
|
end
|
32
34
|
|
@@ -63,19 +65,15 @@ module Citrus
|
|
63
65
|
# module is in fact a grammar.
|
64
66
|
module Grammar
|
65
67
|
# Creates a new anonymous module that includes Grammar. If a +block+ is
|
66
|
-
# provided, it
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# for the rationale behind this decision.
|
70
|
-
#
|
71
|
-
# Grammars created with this method may be assigned a name by being assigned
|
72
|
-
# to some constant, e.g.:
|
68
|
+
# provided, it is +module_eval+'d in the context of the new module. Grammars
|
69
|
+
# created with this method may be assigned a name by being assigned to some
|
70
|
+
# constant, e.g.:
|
73
71
|
#
|
74
72
|
# Calc = Grammar.new {}
|
75
73
|
#
|
76
74
|
def self.new(&block)
|
77
75
|
mod = Module.new { include Grammar }
|
78
|
-
|
76
|
+
mod.module_eval(&block) if block
|
79
77
|
mod
|
80
78
|
end
|
81
79
|
|
@@ -888,7 +886,7 @@ module Citrus
|
|
888
886
|
|
889
887
|
# Checks equality by comparing this match's #text value to +obj+.
|
890
888
|
def ==(obj)
|
891
|
-
|
889
|
+
obj == text
|
892
890
|
end
|
893
891
|
|
894
892
|
alias eql? ==
|
@@ -1,11 +1,10 @@
|
|
1
1
|
require 'citrus'
|
2
2
|
|
3
3
|
module Citrus
|
4
|
-
# A grammar for Citrus
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
8
|
-
module PEG
|
4
|
+
# A grammar for Citrus grammar files. This module is used in Citrus#eval to
|
5
|
+
# parse and evaluate Citrus grammars and serves as a prime example of how to
|
6
|
+
# create a complex grammar complete with semantic interpretation in pure Ruby.
|
7
|
+
module File
|
9
8
|
include Grammar
|
10
9
|
|
11
10
|
## Hierarchical syntax
|
@@ -356,7 +355,6 @@ module Citrus
|
|
356
355
|
rule :require_keyword, [ 'require', :space ]
|
357
356
|
rule :include_keyword, [ 'include', :space ]
|
358
357
|
rule :grammar_keyword, [ 'grammar', :space ]
|
359
|
-
rule :super_keyword, [ 'super', :space ]
|
360
358
|
rule :root_keyword, [ 'root', :space ]
|
361
359
|
rule :rule_keyword, [ 'rule', :space ]
|
362
360
|
rule :end_keyword, [ 'end', :space ]
|
data/test/calc_peg_test.rb
CHANGED
data/test/calc_sugar_test.rb
CHANGED
data/test/calc_test.rb
CHANGED
@@ -1,42 +1,40 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/helper'
|
2
|
-
require 'citrus/
|
2
|
+
require 'citrus/file'
|
3
3
|
|
4
|
-
class
|
4
|
+
class CitrusFileTest < Test::Unit::TestCase
|
5
5
|
|
6
6
|
# A shortcut for creating a grammar that includes Citrus::PEG but uses a
|
7
7
|
# different root.
|
8
8
|
def peg(root_rule)
|
9
9
|
Grammar.new {
|
10
|
-
include Citrus::
|
10
|
+
include Citrus::File
|
11
11
|
root root_rule
|
12
12
|
}
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
15
|
## File tests
|
17
16
|
|
17
|
+
F = ::File
|
18
18
|
|
19
19
|
def run_file_test(file, root)
|
20
20
|
grammar = peg(root)
|
21
|
-
code =
|
21
|
+
code = F.read(file)
|
22
22
|
match = grammar.parse(code)
|
23
23
|
assert(match)
|
24
24
|
end
|
25
25
|
|
26
26
|
%w< rule grammar >.each do |type|
|
27
|
-
Dir[
|
27
|
+
Dir[F.dirname(__FILE__) + "/_files/#{type}*.citrus"].each do |path|
|
28
28
|
module_eval(<<-RUBY.gsub(/^ /, ''), __FILE__, __LINE__ + 1)
|
29
|
-
def test_#{
|
29
|
+
def test_#{F.basename(path, '.citrus')}
|
30
30
|
run_file_test("#{path}", :#{type})
|
31
31
|
end
|
32
32
|
RUBY
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
36
|
## Hierarchical syntax
|
38
37
|
|
39
|
-
|
40
38
|
def test_rule_body
|
41
39
|
grammar = peg(:rule_body)
|
42
40
|
|
data/test/grammar_test.rb
CHANGED
@@ -3,9 +3,9 @@ require File.dirname(__FILE__) + '/helper'
|
|
3
3
|
class GrammarTest < Test::Unit::TestCase
|
4
4
|
|
5
5
|
def test_new
|
6
|
-
Grammar.new do
|
7
|
-
assert_kind_of(Module,
|
8
|
-
assert(
|
6
|
+
Grammar.new do
|
7
|
+
assert_kind_of(Module, self)
|
8
|
+
assert(include?(Grammar))
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
data/test/match_test.rb
CHANGED
@@ -49,6 +49,18 @@ class MatchTest < Test::Unit::TestCase
|
|
49
49
|
assert_equal(11, match.length)
|
50
50
|
end
|
51
51
|
|
52
|
+
def test_equality
|
53
|
+
match1 = Match.new('a')
|
54
|
+
match2 = Match.new('a')
|
55
|
+
assert(match1 == 'a')
|
56
|
+
assert(match1 == match2)
|
57
|
+
assert(match2 == match1)
|
58
|
+
|
59
|
+
match3 = Match.new('b')
|
60
|
+
assert_equal(false, match1 == match3)
|
61
|
+
assert_equal(false, match3 == match1)
|
62
|
+
end
|
63
|
+
|
52
64
|
def test_matches
|
53
65
|
match = Double.parse('123')
|
54
66
|
assert(match)
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 1.0.0
|
9
|
+
version: 1.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Michael Jackson
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-18 00:00:00 -06:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -51,12 +51,13 @@ extra_rdoc_files:
|
|
51
51
|
- README
|
52
52
|
files:
|
53
53
|
- lib/citrus/debug.rb
|
54
|
-
- lib/citrus/
|
54
|
+
- lib/citrus/file.rb
|
55
55
|
- lib/citrus/sugar.rb
|
56
56
|
- lib/citrus.rb
|
57
57
|
- examples/calc.citrus
|
58
58
|
- examples/calc.rb
|
59
59
|
- examples/calc_sugar.rb
|
60
|
+
- extras/citrus.vim
|
60
61
|
- test/alias_test.rb
|
61
62
|
- test/and_predicate_test.rb
|
62
63
|
- test/calc_peg_test.rb
|
@@ -64,13 +65,13 @@ files:
|
|
64
65
|
- test/calc_test.rb
|
65
66
|
- test/choice_test.rb
|
66
67
|
- test/expression_test.rb
|
68
|
+
- test/file_test.rb
|
67
69
|
- test/fixed_width_test.rb
|
68
70
|
- test/grammar_test.rb
|
69
71
|
- test/helper.rb
|
70
72
|
- test/label_test.rb
|
71
73
|
- test/match_test.rb
|
72
74
|
- test/not_predicate_test.rb
|
73
|
-
- test/peg_test.rb
|
74
75
|
- test/repeat_test.rb
|
75
76
|
- test/rule_test.rb
|
76
77
|
- test/sequence_test.rb
|
@@ -121,12 +122,12 @@ test_files:
|
|
121
122
|
- test/calc_test.rb
|
122
123
|
- test/choice_test.rb
|
123
124
|
- test/expression_test.rb
|
125
|
+
- test/file_test.rb
|
124
126
|
- test/fixed_width_test.rb
|
125
127
|
- test/grammar_test.rb
|
126
128
|
- test/label_test.rb
|
127
129
|
- test/match_test.rb
|
128
130
|
- test/not_predicate_test.rb
|
129
|
-
- test/peg_test.rb
|
130
131
|
- test/repeat_test.rb
|
131
132
|
- test/rule_test.rb
|
132
133
|
- test/sequence_test.rb
|