less 0.8.13 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +10 -1
- data/Rakefile +21 -3
- data/VERSION +1 -1
- data/bin/lessc +0 -8
- data/less.gemspec +138 -15
- data/lib/less.rb +67 -11
- data/lib/less/command.rb +24 -25
- data/lib/less/engine.rb +36 -140
- data/lib/less/engine/builder.rb +8 -0
- data/lib/less/engine/less.tt +374 -0
- data/lib/less/engine/nodes.rb +8 -0
- data/lib/less/engine/nodes/element.rb +150 -0
- data/lib/less/engine/nodes/entity.rb +73 -0
- data/lib/less/engine/nodes/function.rb +82 -0
- data/lib/less/engine/nodes/literal.rb +135 -0
- data/lib/less/engine/nodes/property.rb +112 -0
- data/lib/less/engine/nodes/selector.rb +39 -0
- data/lib/less/engine/parser.rb +3860 -0
- data/lib/vendor/treetop/.gitignore +7 -0
- data/lib/vendor/treetop/LICENSE +19 -0
- data/lib/vendor/treetop/README +164 -0
- data/lib/vendor/treetop/Rakefile +19 -0
- data/lib/vendor/treetop/benchmark/seqpar.gnuplot +15 -0
- data/lib/vendor/treetop/benchmark/seqpar.treetop +16 -0
- data/lib/vendor/treetop/benchmark/seqpar_benchmark.rb +107 -0
- data/lib/vendor/treetop/bin/tt +28 -0
- data/lib/vendor/treetop/lib/treetop.rb +8 -0
- data/lib/vendor/treetop/lib/treetop/bootstrap_gen_1_metagrammar.rb +45 -0
- data/lib/vendor/treetop/lib/treetop/compiler.rb +6 -0
- data/lib/vendor/treetop/lib/treetop/compiler/grammar_compiler.rb +42 -0
- data/lib/vendor/treetop/lib/treetop/compiler/lexical_address_space.rb +17 -0
- data/lib/vendor/treetop/lib/treetop/compiler/metagrammar.rb +3097 -0
- data/lib/vendor/treetop/lib/treetop/compiler/metagrammar.treetop +408 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes.rb +19 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/anything_symbol.rb +18 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/atomic_expression.rb +14 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/character_class.rb +23 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/choice.rb +31 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/declaration_sequence.rb +24 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/grammar.rb +28 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/inline_module.rb +27 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/nonterminal.rb +13 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/optional.rb +19 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parenthesized_expression.rb +9 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parsing_expression.rb +146 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parsing_rule.rb +55 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/predicate.rb +45 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/repetition.rb +55 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/sequence.rb +68 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/terminal.rb +20 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/transient_prefix.rb +9 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/treetop_file.rb +9 -0
- data/lib/vendor/treetop/lib/treetop/compiler/ruby_builder.rb +113 -0
- data/lib/vendor/treetop/lib/treetop/ruby_extensions.rb +2 -0
- data/lib/vendor/treetop/lib/treetop/ruby_extensions/string.rb +42 -0
- data/lib/vendor/treetop/lib/treetop/runtime.rb +5 -0
- data/lib/vendor/treetop/lib/treetop/runtime/compiled_parser.rb +109 -0
- data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list.rb +4 -0
- data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/head_node.rb +15 -0
- data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/interval_skip_list.rb +200 -0
- data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/node.rb +164 -0
- data/lib/vendor/treetop/lib/treetop/runtime/syntax_node.rb +90 -0
- data/lib/vendor/treetop/lib/treetop/runtime/terminal_parse_failure.rb +16 -0
- data/lib/vendor/treetop/lib/treetop/runtime/terminal_syntax_node.rb +17 -0
- data/lib/vendor/treetop/lib/treetop/version.rb +9 -0
- data/lib/vendor/treetop/spec/compiler/and_predicate_spec.rb +36 -0
- data/lib/vendor/treetop/spec/compiler/anything_symbol_spec.rb +44 -0
- data/lib/vendor/treetop/spec/compiler/character_class_spec.rb +247 -0
- data/lib/vendor/treetop/spec/compiler/choice_spec.rb +80 -0
- data/lib/vendor/treetop/spec/compiler/circular_compilation_spec.rb +28 -0
- data/lib/vendor/treetop/spec/compiler/failure_propagation_functional_spec.rb +21 -0
- data/lib/vendor/treetop/spec/compiler/grammar_compiler_spec.rb +84 -0
- data/lib/vendor/treetop/spec/compiler/grammar_spec.rb +41 -0
- data/lib/vendor/treetop/spec/compiler/nonterminal_symbol_spec.rb +40 -0
- data/lib/vendor/treetop/spec/compiler/not_predicate_spec.rb +38 -0
- data/lib/vendor/treetop/spec/compiler/one_or_more_spec.rb +35 -0
- data/lib/vendor/treetop/spec/compiler/optional_spec.rb +37 -0
- data/lib/vendor/treetop/spec/compiler/parenthesized_expression_spec.rb +19 -0
- data/lib/vendor/treetop/spec/compiler/parsing_rule_spec.rb +32 -0
- data/lib/vendor/treetop/spec/compiler/sequence_spec.rb +115 -0
- data/lib/vendor/treetop/spec/compiler/terminal_spec.rb +81 -0
- data/lib/vendor/treetop/spec/compiler/terminal_symbol_spec.rb +37 -0
- data/lib/vendor/treetop/spec/compiler/test_grammar.treetop +7 -0
- data/lib/vendor/treetop/spec/compiler/test_grammar.tt +7 -0
- data/lib/vendor/treetop/spec/compiler/test_grammar_do.treetop +7 -0
- data/lib/vendor/treetop/spec/compiler/zero_or_more_spec.rb +56 -0
- data/lib/vendor/treetop/spec/composition/a.treetop +11 -0
- data/lib/vendor/treetop/spec/composition/b.treetop +11 -0
- data/lib/vendor/treetop/spec/composition/c.treetop +10 -0
- data/lib/vendor/treetop/spec/composition/d.treetop +10 -0
- data/lib/vendor/treetop/spec/composition/f.treetop +17 -0
- data/lib/vendor/treetop/spec/composition/grammar_composition_spec.rb +40 -0
- data/lib/vendor/treetop/spec/composition/subfolder/e_includes_c.treetop +15 -0
- data/lib/vendor/treetop/spec/ruby_extensions/string_spec.rb +32 -0
- data/lib/vendor/treetop/spec/runtime/compiled_parser_spec.rb +101 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/delete_spec.rb +147 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/expire_range_spec.rb +349 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/insert_and_delete_node.rb +385 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/insert_spec.rb +660 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.graffle +6175 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.rb +58 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture.rb +23 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture_spec.rb +164 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/spec_helper.rb +84 -0
- data/lib/vendor/treetop/spec/runtime/syntax_node_spec.rb +68 -0
- data/lib/vendor/treetop/spec/spec_helper.rb +106 -0
- data/lib/vendor/treetop/spec/spec_suite.rb +4 -0
- data/lib/vendor/treetop/treetop.gemspec +17 -0
- data/spec/command_spec.rb +2 -6
- data/spec/css/accessors-1.0.css +18 -0
- data/spec/css/big-1.0.css +3768 -0
- data/spec/css/comments-1.0.css +9 -0
- data/spec/css/css-1.0.css +40 -0
- data/spec/css/functions-1.0.css +6 -0
- data/spec/css/import-1.0.css +11 -0
- data/spec/css/mixins-1.0.css +28 -0
- data/spec/css/operations-1.0.css +28 -0
- data/spec/css/rulesets-1.0.css +17 -0
- data/spec/css/scope-1.0.css +14 -0
- data/spec/css/strings-1.0.css +12 -0
- data/spec/css/variables-1.0.css +6 -0
- data/spec/css/whitespace-1.0.css +9 -0
- data/spec/engine_spec.rb +66 -18
- data/spec/less/accessors-1.0.less +20 -0
- data/spec/less/big-1.0.less +4810 -0
- data/spec/less/colors-1.0.less +0 -0
- data/spec/less/comments-1.0.less +46 -0
- data/spec/less/css-1.0.less +84 -0
- data/spec/less/exceptions/mixed-units-error.less +3 -0
- data/spec/less/exceptions/name-error-1.0.less +3 -0
- data/spec/less/exceptions/syntax-error-1.0.less +3 -0
- data/spec/less/functions-1.0.less +6 -0
- data/spec/less/import-1.0.less +7 -0
- data/spec/less/import/import-test-a.less +2 -0
- data/spec/less/import/import-test-b.less +8 -0
- data/spec/less/import/import-test-c.less +5 -0
- data/spec/less/mixins-1.0.less +43 -0
- data/spec/less/operations-1.0.less +39 -0
- data/spec/less/rulesets-1.0.less +30 -0
- data/spec/less/scope-1.0.less +33 -0
- data/spec/less/strings-1.0.less +14 -0
- data/spec/less/variables-1.0.less +18 -0
- data/spec/less/whitespace-1.0.less +21 -0
- data/spec/spec.css +79 -24
- data/spec/spec.less +2 -3
- data/spec/spec_helper.rb +4 -1
- metadata +136 -13
- data/lib/less/tree.rb +0 -82
- data/spec/css/less-0.8.10.css +0 -30
- data/spec/css/less-0.8.11.css +0 -31
- data/spec/css/less-0.8.12.css +0 -28
- data/spec/css/less-0.8.5.css +0 -24
- data/spec/css/less-0.8.6.css +0 -24
- data/spec/css/less-0.8.7.css +0 -24
- data/spec/css/less-0.8.8.css +0 -25
- data/spec/tree_spec.rb +0 -5
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2007 Nathan Sobo.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
@@ -0,0 +1,164 @@
|
|
1
|
+
Tutorial
|
2
|
+
========
|
3
|
+
Languages can be split into two components, their *syntax* and their *semantics*. It's your understanding of English syntax that tells you the stream of words "Sleep furiously green ideas colorless" is not a valid sentence. Semantics is deeper. Even if we rearrange the above sentence to be "Colorless green ideas sleep furiously", which is syntactically correct, it remains nonsensical on a semantic level. With Treetop, you'll be dealing with languages that are much simpler than English, but these basic concepts apply. Your programs will need to address both the syntax and the semantics of the languages they interpret.
|
4
|
+
|
5
|
+
Treetop equips you with powerful tools for each of these two aspects of interpreter writing. You'll describe the syntax of your language with a *parsing expression grammar*. From this description, Treetop will generate a Ruby parser that transforms streams of characters written into your language into *abstract syntax trees* representing their structure. You'll then describe the semantics of your language in Ruby by defining methods on the syntax trees the parser generates.
|
6
|
+
|
7
|
+
Parsing Expression Grammars, The Basics
|
8
|
+
=======================================
|
9
|
+
The first step in using Treetop is defining a grammar in a file with the `.treetop` extension. Here's a grammar that's useless because it's empty:
|
10
|
+
|
11
|
+
# my_grammar.treetop
|
12
|
+
grammar MyGrammar
|
13
|
+
end
|
14
|
+
|
15
|
+
Next, you start filling your grammar with rules. Each rule associates a name with a parsing expression, like the following:
|
16
|
+
|
17
|
+
# my_grammar.treetop
|
18
|
+
# You can use a .tt extension instead if you wish
|
19
|
+
grammar MyGrammar
|
20
|
+
rule hello
|
21
|
+
'hello chomsky'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
The first rule becomes the *root* of the grammar, causing its expression to be matched when a parser for the grammar is fed a string. The above grammar can now be used in a Ruby program. Notice how a string matching the first rule parses successfully, but a second nonmatching string does not.
|
26
|
+
|
27
|
+
# use_grammar.rb
|
28
|
+
require 'rubygems'
|
29
|
+
require 'treetop'
|
30
|
+
Treetop.load 'my_grammar'
|
31
|
+
# or just:
|
32
|
+
# require 'my_grammar' # This works because Polyglot hooks "require" to find and load Treetop files
|
33
|
+
|
34
|
+
parser = MyGrammarParser.new
|
35
|
+
puts parser.parse('hello chomsky') # => Treetop::Runtime::SyntaxNode
|
36
|
+
puts parser.parse('silly generativists!') # => nil
|
37
|
+
|
38
|
+
Users of *regular expressions* will find parsing expressions familiar. They share the same basic purpose, matching strings against patterns. However, parsing expressions can recognize a broader category of languages than their less expressive brethren. Before we get into demonstrating that, lets cover some basics. At first parsing expressions won't seem much different. Trust that they are.
|
39
|
+
|
40
|
+
Terminal Symbols
|
41
|
+
----------------
|
42
|
+
The expression in the grammar above is a terminal symbol. It will only match a string that matches it exactly. There are two other kinds of terminal symbols, which we'll revisit later. Terminals are called *atomic expressions* because they aren't composed of smaller expressions.
|
43
|
+
|
44
|
+
Ordered Choices
|
45
|
+
---------------
|
46
|
+
Ordered choices are *composite expressions*, which allow for any of several subexpressions to be matched. These should be familiar from regular expressions, but in parsing expressions, they are delimited by the `/` character. Its important to note that the choices are prioritized in the order they appear. If an earlier expression is matched, no subsequent expressions are tried. Here's an example:
|
47
|
+
|
48
|
+
# my_grammar.treetop
|
49
|
+
grammar MyGrammar
|
50
|
+
rule hello
|
51
|
+
'hello chomsky' / 'hello lambek'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# fragment of use_grammar.rb
|
56
|
+
puts parser.parse('hello chomsky') # => Treetop::Runtime::SyntaxNode
|
57
|
+
puts parser.parse('hello lambek') # => Treetop::Runtime::SyntaxNode
|
58
|
+
puts parser.parse('silly generativists!') # => nil
|
59
|
+
|
60
|
+
Note that once a choice rule has matched the text using a particular alternative at a particular location in the input and hence has succeeded, that choice will never be reconsidered, even if the chosen alternative causes another rule to fail where a later alternative wouldn't have. It's always a later alternative, since the first to succeed is final - why keep looking when you've found what you wanted? This is a feature of PEG parsers that you need to understand if you're going to succeed in using Treetop. In order to memoize success and failures, such decisions cannot be reversed. Luckily Treetop provides a variety of clever ways you can tell it to avoid making the wrong decisions. But more on that later.
|
61
|
+
|
62
|
+
Sequences
|
63
|
+
---------
|
64
|
+
Sequences are composed of other parsing expressions separated by spaces. Using sequences, we can tighten up the above grammar.
|
65
|
+
|
66
|
+
# my_grammar.treetop
|
67
|
+
grammar MyGrammar
|
68
|
+
rule hello
|
69
|
+
'hello ' ('chomsky' / 'lambek')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
Note the use of parentheses to override the default precedence rules, which bind sequences more tightly than choices.
|
74
|
+
|
75
|
+
Once the whole sequence has been matched, the result is memoized and the details of the match will not be reconsidered for that location in the input.
|
76
|
+
|
77
|
+
Nonterminal Symbols
|
78
|
+
-------------------
|
79
|
+
Here we leave regular expressions behind. Nonterminals allow expressions to refer to other expressions by name. A trivial use of this facility would allow us to make the above grammar more readable should the list of names grow longer.
|
80
|
+
|
81
|
+
# my_grammar.treetop
|
82
|
+
grammar MyGrammar
|
83
|
+
rule hello
|
84
|
+
'hello ' linguist
|
85
|
+
end
|
86
|
+
|
87
|
+
rule linguist
|
88
|
+
'chomsky' / 'lambek' / 'jacobsen' / 'frege'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
The true power of this facility, however, is unleashed when writing *recursive expressions*. Here is a self-referential expression that can match any number of open parentheses followed by any number of closed parentheses. This is theoretically impossible with regular expressions due to the *pumping lemma*.
|
93
|
+
|
94
|
+
# parentheses.treetop
|
95
|
+
grammar Parentheses
|
96
|
+
rule parens
|
97
|
+
'(' parens ')' / ''
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
The `parens` expression simply states that a `parens` is a set of parentheses surrounding another `parens` expression or, if that doesn't match, the empty string. If you are uncomfortable with recursion, its time to get comfortable, because it is the basis of language. Here's a tip: Don't try and imagine the parser circling round and round through the same rule. Instead, imagine the rule is *already* defined while you are defining it. If you imagine that `parens` already matches a string of matching parentheses, then its easy to think of `parens` as an open and closing parentheses around another set of matching parentheses, which conveniently, you happen to be defining. You know that `parens` is supposed to represent a string of matched parentheses, so trust in that meaning, even if you haven't fully implemented it yet.
|
103
|
+
|
104
|
+
Repetition
|
105
|
+
----------
|
106
|
+
Any item in a rule may be followed by a '+' or a '*' character, signifying one-or-more and zero-or-more occurrences of that item. Beware though; the match is greedy, and if it matches too many items and causes subsequent items in the sequence to fail, the number matched will never be reconsidered. Here's a simple example of a rule that will never succeed:
|
107
|
+
|
108
|
+
# toogreedy.treetop
|
109
|
+
grammar TooGreedy
|
110
|
+
rule a_s
|
111
|
+
'a'* 'a'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
The 'a'* will always eat up any 'a's that follow, and the subsequent 'a' will find none there, so the whole rule will fail. You might need to use lookahead to avoid matching too much.
|
116
|
+
|
117
|
+
Negative Lookahead
|
118
|
+
------------------
|
119
|
+
|
120
|
+
When you need to ensure that the following item *doesn't* match in some case where it might otherwise, you can use negat!ve lookahead, which is an item preceeded by a ! - here's an example:
|
121
|
+
|
122
|
+
# postcondition.treetop
|
123
|
+
grammar PostCondition
|
124
|
+
rule conditional_sentence
|
125
|
+
( !conditional_keyword word )+ conditional_keyword [ \t]+ word*
|
126
|
+
end
|
127
|
+
|
128
|
+
rule word
|
129
|
+
([a-zA-Z]+ [ \t]+)
|
130
|
+
end
|
131
|
+
|
132
|
+
rule conditional_keyword
|
133
|
+
'if' / 'while' / 'until'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
Even though the rule `word` would match any of the conditional keywords, the first words of a conditional_sentence must not be conditional_keywords. The negative lookahead prevents that matching, and prevents the repetition from matching too much input. Note that the lookahead may be a grammar rule of any complexity, including one that isn't used elsewhere in your grammar.
|
138
|
+
|
139
|
+
Positive lookahead
|
140
|
+
------------------
|
141
|
+
|
142
|
+
Sometimes you want an item to match, but only if the *following* text would match some pattern. You don't want to consume that following text, but if it's not there, you want this rule to fail. You can append a positive lookahead like this to a rule by appending the lookahead rule preceeded by an & character.
|
143
|
+
|
144
|
+
|
145
|
+
|
146
|
+
Features to cover in the talk
|
147
|
+
=============================
|
148
|
+
|
149
|
+
* Treetop files
|
150
|
+
* Grammar definition
|
151
|
+
* Rules
|
152
|
+
* Loading a grammar
|
153
|
+
* Compiling a grammar with the `tt` command
|
154
|
+
* Accessing a parser for the grammar from Ruby
|
155
|
+
* Parsing Expressions of all kinds
|
156
|
+
? Left recursion and factorization
|
157
|
+
- Here I can talk about function application, discussing how the operator
|
158
|
+
could be an arbitrary expression
|
159
|
+
* Inline node class eval blocks
|
160
|
+
* Node class declarations
|
161
|
+
* Labels
|
162
|
+
* Use of super within within labels
|
163
|
+
* Grammar composition with include
|
164
|
+
* Use of super with grammar composition
|
@@ -0,0 +1,19 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
$LOAD_PATH.unshift(File.join(dir, 'vendor', 'rspec', 'lib'))
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
|
7
|
+
require 'rake/gempackagetask'
|
8
|
+
|
9
|
+
task :default => :spec
|
10
|
+
|
11
|
+
Spec::Rake::SpecTask.new do |t|
|
12
|
+
t.pattern = 'spec/**/*spec.rb'
|
13
|
+
end
|
14
|
+
|
15
|
+
load "./treetop.gemspec"
|
16
|
+
|
17
|
+
Rake::GemPackageTask.new($gemspec) do |pkg|
|
18
|
+
pkg.need_tar = true
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
f1(x) = a*x
|
2
|
+
a = 0.5
|
3
|
+
fit f1(x) 'before.dat' using 1:2 via a
|
4
|
+
|
5
|
+
f2(x) = b*x
|
6
|
+
b = 0.5
|
7
|
+
fit f2(x) 'after.dat' using 1:2 via b
|
8
|
+
|
9
|
+
set xlabel "Length of input"
|
10
|
+
set ylabel "CPU time to parse"
|
11
|
+
|
12
|
+
plot a*x title 'a*x (Before)',\
|
13
|
+
b*x title 'b*x (After)',\
|
14
|
+
"before.dat" using 1:2 title 'Before', \
|
15
|
+
"after.dat" using 1:2 title 'After'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
grammar SeqPar
|
2
|
+
|
3
|
+
rule statement
|
4
|
+
'par ' (statement ' ')+ 'end'
|
5
|
+
/ 'sequence' ' ' (statement ' ')+ 'end'
|
6
|
+
/ 'seq' ' ' (statement ' ')+ 'end'
|
7
|
+
/ ('fit' [\s] (statement ' ')+ 'end') {
|
8
|
+
def foo
|
9
|
+
"foo"
|
10
|
+
end
|
11
|
+
}
|
12
|
+
/ 'art'+ [ ] (statement ' ')+ 'end'
|
13
|
+
/ [A-Z] [a-zA-z0-9]*
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# Benchmarking written by Bernard Lambeau, improved by Jason Garber
|
2
|
+
#
|
3
|
+
# To test your optimizations:
|
4
|
+
# 1. Run ruby seqpar_benchmark.rb
|
5
|
+
# 2. cp after.dat before.dat
|
6
|
+
# 3. Make your modifications to the treetop code
|
7
|
+
# 4. Run ruby seqpar_benchmark.rb
|
8
|
+
# 5. Run gnuplot seqpar.gnuplot
|
9
|
+
#
|
10
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
11
|
+
require 'treetop'
|
12
|
+
require 'benchmark'
|
13
|
+
|
14
|
+
srand(47562) # So it runs the same each time
|
15
|
+
|
16
|
+
class Array; def sum; inject( nil ) { |sum,x| sum ? sum+x : x }; end; end
|
17
|
+
class Array; def mean; sum / size; end; end
|
18
|
+
|
19
|
+
class SeqParBenchmark
|
20
|
+
|
21
|
+
OPERATORS = ["seq", "fit", "art"*5, "par", "sequence"]
|
22
|
+
|
23
|
+
# Loads the grammar and returns a parser
|
24
|
+
def initialize
|
25
|
+
compiler = Treetop::Compiler::GrammarCompiler.new
|
26
|
+
@where = File.expand_path(File.dirname(__FILE__))
|
27
|
+
grammar = File.join(@where, 'seqpar.treetop')
|
28
|
+
output = File.join(@where, 'seqpar.rb')
|
29
|
+
compiler.compile(grammar, output)
|
30
|
+
load output
|
31
|
+
File.delete(output)
|
32
|
+
@parser = SeqParParser.new
|
33
|
+
end
|
34
|
+
|
35
|
+
# Checks the grammar
|
36
|
+
def check
|
37
|
+
["Task",
|
38
|
+
"seq Task end",
|
39
|
+
"par Task end",
|
40
|
+
"seq Task Task end",
|
41
|
+
"par Task Task end",
|
42
|
+
"par seq Task end Task end",
|
43
|
+
"par seq seq Task end end Task end",
|
44
|
+
"seq Task par seq Task end Task end Task end"].each do |input|
|
45
|
+
raise ParseError.new(@parser) if @parser.parse(input).nil?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Generates an input text
|
50
|
+
def generate(depth=0)
|
51
|
+
return "Task" if depth>7
|
52
|
+
return "seq #{generate(depth+1)} end" if depth==0
|
53
|
+
which = rand(OPERATORS.length)
|
54
|
+
case which
|
55
|
+
when 0
|
56
|
+
"Task"
|
57
|
+
else
|
58
|
+
raise unless OPERATORS[which]
|
59
|
+
buffer = "#{OPERATORS[which]} "
|
60
|
+
0.upto(rand(4)+1) do
|
61
|
+
buffer << generate(depth+1) << " "
|
62
|
+
end
|
63
|
+
buffer << "end"
|
64
|
+
buffer
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Launches benchmarking
|
69
|
+
def benchmark
|
70
|
+
number_by_size = Hash.new {|h,k| h[k] = 0}
|
71
|
+
time_by_size = Hash.new {|h,k| h[k] = 0}
|
72
|
+
0.upto(250) do |i|
|
73
|
+
input = generate
|
74
|
+
length = input.length
|
75
|
+
puts "Launching #{i}: #{input.length}"
|
76
|
+
# puts input
|
77
|
+
tms = Benchmark.measure { @parser.parse(input) }
|
78
|
+
number_by_size[length] += 1
|
79
|
+
time_by_size[length] += tms.total*1000
|
80
|
+
end
|
81
|
+
# puts number_by_size.inspect
|
82
|
+
# puts time_by_size.inspect
|
83
|
+
|
84
|
+
File.open(File.join(@where, 'after.dat'), 'w') do |dat|
|
85
|
+
number_by_size.keys.sort.each do |size|
|
86
|
+
dat << "#{size} #{(time_by_size[size]/number_by_size[size]).truncate}\n"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if File.exists?(File.join(@where, 'before.dat'))
|
91
|
+
before = {}
|
92
|
+
performance_increases = []
|
93
|
+
File.foreach(File.join(@where, 'before.dat')) do |line|
|
94
|
+
size, time = line.split(' ')
|
95
|
+
before[size] = time
|
96
|
+
end
|
97
|
+
File.foreach(File.join(@where, 'after.dat')) do |line|
|
98
|
+
size, time = line.split(' ')
|
99
|
+
performance_increases << (before[size].to_f - time.to_f) / before[size].to_f unless time == "0"
|
100
|
+
end
|
101
|
+
puts "Average performance increase: #{performance_increases.mean * 100}%"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
SeqParBenchmark.new.benchmark
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'treetop'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
|
6
|
+
require 'treetop'
|
7
|
+
|
8
|
+
if ARGV.empty?
|
9
|
+
puts "Usage:\n\ntt foo.treetop bar.treetop ...\n or\ntt foo.treetop -o alternate_name.rb\n\n"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
compiler = Treetop::Compiler::GrammarCompiler.new
|
14
|
+
|
15
|
+
while !ARGV.empty?
|
16
|
+
treetop_file = ARGV.shift
|
17
|
+
if !File.exist?(treetop_file)
|
18
|
+
puts "Error: file '#{treetop_file}' doesn't exist\n\n"
|
19
|
+
exit(2)
|
20
|
+
end
|
21
|
+
if ARGV.size >= 2 && ARGV[1] == '-o'
|
22
|
+
ARGV.shift # explicit output file name option
|
23
|
+
compiler.compile(treetop_file, ARGV.shift)
|
24
|
+
else
|
25
|
+
# list of input files option
|
26
|
+
compiler.compile(treetop_file)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# This file's job is to load a Treetop::Compiler::Metagrammar and Treetop::Compiler::MetagrammarParser
|
2
|
+
# into the environment by compiling the current metagrammar.treetop using a trusted version of Treetop.
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
dir = File.dirname(__FILE__)
|
6
|
+
|
7
|
+
TREETOP_VERSION_REQUIRED_TO_BOOTSTRAP = '>= 1.1.5'
|
8
|
+
|
9
|
+
# Loading trusted version of Treetop to compile the compiler
|
10
|
+
gem_spec = Gem.source_index.find_name('treetop', TREETOP_VERSION_REQUIRED_TO_BOOTSTRAP).last
|
11
|
+
raise "Install a Treetop Gem version #{TREETOP_VERSION_REQUIRED_TO_BOOTSTRAP} to bootstrap." unless gem_spec
|
12
|
+
trusted_treetop_path = gem_spec.full_gem_path
|
13
|
+
require File.join(trusted_treetop_path, 'lib', 'treetop')
|
14
|
+
|
15
|
+
# Relocating trusted version of Treetop to Trusted::Treetop
|
16
|
+
Trusted = Module.new
|
17
|
+
Trusted::Treetop = Treetop
|
18
|
+
Object.send(:remove_const, :Treetop)
|
19
|
+
Object.send(:remove_const, :TREETOP_ROOT)
|
20
|
+
|
21
|
+
# Requiring version of Treetop that is under test
|
22
|
+
$exclude_metagrammar = true
|
23
|
+
require File.expand_path(File.join(dir, '..', 'treetop'))
|
24
|
+
|
25
|
+
# Compile and evaluate freshly generated metagrammar source
|
26
|
+
METAGRAMMAR_PATH = File.join(TREETOP_ROOT, 'compiler', 'metagrammar.treetop')
|
27
|
+
compiled_metagrammar_source = Trusted::Treetop::Compiler::GrammarCompiler.new.ruby_source(METAGRAMMAR_PATH)
|
28
|
+
Object.class_eval(compiled_metagrammar_source)
|
29
|
+
|
30
|
+
# The compiler under test was compiled with the trusted grammar and therefore depends on its runtime
|
31
|
+
# But the runtime in the global namespace is the new runtime. We therefore inject the trusted runtime
|
32
|
+
# into the compiler so its parser functions correctly. It will still not work for custom classes that
|
33
|
+
# explicitly subclass the wrong runtime. For now I am working around this by keeping 1 generation of
|
34
|
+
# backward compatibility in these cases.
|
35
|
+
# Treetop::Compiler::Metagrammar.module_eval do
|
36
|
+
# include Trusted::Treetop::Runtime
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# Treetop::Compiler.send(:remove_const, :MetagrammarParser)
|
40
|
+
# class Treetop::Compiler::MetagrammarParser < Trusted::Treetop::Runtime::CompiledParser
|
41
|
+
# include Treetop::Compiler::Metagrammar
|
42
|
+
# include Trusted::Treetop::Runtime
|
43
|
+
# end
|
44
|
+
|
45
|
+
$bootstrapped_gen_1_metagrammar = true
|
@@ -0,0 +1,6 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require File.join(dir, *%w[compiler lexical_address_space])
|
3
|
+
require File.join(dir, *%w[compiler ruby_builder])
|
4
|
+
require File.join(dir, *%w[compiler node_classes])
|
5
|
+
require File.join(dir, *%w[compiler metagrammar]) unless $exclude_metagrammar
|
6
|
+
require File.join(dir, *%w[compiler grammar_compiler])
|