story-gen 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/NEWS ADDED
@@ -0,0 +1,13 @@
1
+ 0.1.0:
2
+ "-m" or "--allow-comma-in-facts" is removed
3
+ Comma (",") in Facts is always allowed
4
+ $DEBUG variable is considered in Parse
5
+ Some bugs fixed
6
+ "Either" statement is changed
7
+ Dot (".") is now allowed at the end of the story only
8
+ "If" statement syntax is completely rewritten
9
+ No more commas in "times", "while", "for all" statements
10
+ Alternate form of compound statement (with "-") is removed
11
+
12
+ 0.0.4:
13
+ Like, first release
data/README.md CHANGED
@@ -55,7 +55,7 @@ A story is based on Facts. To state a Fact just write it:
55
55
 
56
56
  "John" loves "Liza"
57
57
 
58
- In the Fact expression you may use english and russian words (except keywords, see below), characters from "#№@$%/-", integer numbers (e.g., `18`) and arbitrary double- or single-quoted strings (`"..."` or `'...'`). Trailing commas are ignored (`xxx yyy zzz,` is the same as `xxx yyy zzz`). The words are case-insensitive (so `loves` and `Loves` mean the same), quoted strings are case-sensitive (so `"John"`≠`"JOHN"`).
58
+ In the Fact expression you may use english and russian words (except keywords, see below), characters from "#№@$%/-,[]{}", integer numbers (e.g., `18`) and arbitrary double- or single-quoted strings (`"..."` or `'...'`). Trailing commas are ignored (`xxx yyy zzz,` is the same as `xxx yyy zzz`). The words are case-insensitive (so `loves` and `Loves` mean the same), quoted strings are case-sensitive (so `"John"`≠`"JOHN"`).
59
59
 
60
60
  Let's state some Facts:
61
61
 
@@ -77,7 +77,11 @@ What can Facts be used for? You may form conditions with them:
77
77
 
78
78
  If X is a boy then "Let's meet " X;
79
79
 
80
- Here `X` is a variable. Variable names consist of underscores ("_") and capital letters only (e.g., `LONG_VARIABLE_NAME`). You may only capture quoted strings or numbers as variables, so the following condition is invalid:
80
+ Or, with a comma (",") before `then`:
81
+
82
+ If X is a boy, then "Let's meet " X;
83
+
84
+ Here `X` is a variable. Variable names consist of underscores ("_") and capital letters only (e.g., `LONG_VARIABLE_NAME`). You may only capture quoted strings or numbers as variables, so the following is invalid:
81
85
 
82
86
  If "John" A "Liza" then ... /* ERROR! */
83
87
 
@@ -107,47 +111,45 @@ Not all combinations of `and`, `or` and `not` are available, though. Use common
107
111
 
108
112
  If X is not a boy then "How can I determine "X"?" /* ERROR! */
109
113
 
114
+ <!-- TODO: Allow `not` in top-level but disallow to capture variables from it. -->
110
115
  You may also compare variables in the condition:
111
116
 
112
- If X is a boy and Y is a boy and X != Y then
117
+ If X is a boy and Y is a boy and X <> Y then
113
118
  X" and "Y" are two different boys!"
114
119
 
115
120
  There are limitations on the comparison: the comparison must be after the `and` keyword and all variables must be mentioned in the left part of `and`.
116
121
 
117
- You may use `=`, `!=`, `<>`, `<=`, `<`, `>` and `>=` as comparison operators. Take types of the comparands into account, though!
122
+ You may use `=`, `!=`, `<>`, `=/=`, `<=`, `<`, `>` and `>=` as comparison operators. Take types of the comparands into account, though!
118
123
 
119
124
  You may use asterisk ("*") instead of the variable to avoid capturing:
120
125
 
121
126
  If X is a boy and X not loves * then
122
127
  X" is a lonely boy."
123
128
 
124
- You may combine several `if`-s with `or` keyword:
129
+ You may use `otherwise` keyword:
125
130
 
126
- If X is a boy then
127
- "We have found a boy "X"!"
128
- or if Y is a girl then
129
- "We have found a girl "Y"!"
131
+ If X is a boy and X not loves * then
132
+ X" is a lonely boy."
133
+ otherwise
134
+ X" has found someone!"
130
135
 
131
- Or combine `if`-s with some statement:
136
+ You may combine several conditions and `then` statements using colon (":") and dashes ("-"):
132
137
 
133
- If X is a boy then
134
- "We know a boy "X"!"
135
- or if Y is a girl then
136
- "We know a girl "Y"!"
137
- or
138
- "We do not know anyone."
138
+ If:
139
+ - X is a boy then "We have found a boy "X;
140
+ - Y is a girl then "We have found a girl "Y;
141
+ - otherwise "We do not know anyone";
139
142
 
140
- This is like a classical `if ... else if ... else ...` but if multiple conditions are true then random one is chosen (instead of the first one, like in the classical `if-else`). The last `or` is the same as `else` in the classical `if-else` - it is chosen if all conditions are false.
143
+ This is like a classical `if ... else if ... else ...` but if multiple conditions are true then the random one is chosen (instead of the first one, like in the classical `if-else`). The last `otherwise` is the same as `else` in the classical `if-else` - it is chosen if all other conditions are false.
141
144
 
142
- The full form of `if`:
145
+ Look at the trailing semicolons (";") by the way - they resolve ambiguity!
143
146
 
144
- If <condition> [,] then <statement> [,]
145
- or if <condition> [,] then <statement> [,]
146
- or if <condition> [,] then <statement> [,]
147
- ...
148
- or <statement>
147
+ Another form of `if`:
149
148
 
150
- Here `[,]` means "optional comma".
149
+ If:
150
+ a) X is a boy then "We have found a boy "X;
151
+ b) Y is a girl then "We have found a girl "Y;
152
+ c) otherwise "We do not know anyone";
151
153
 
152
154
  You may use captured variables to state a Fact:
153
155
 
@@ -164,11 +166,11 @@ Set multiple Facts false:
164
166
  If X is a boy then
165
167
  X not loves *
166
168
 
167
- You may use the captured variables in another conditions (by prefixing them with hat "^"):
169
+ You may use the captured variables in another conditions by prefixing them with hat ("^"):
168
170
 
169
171
  If X is a boy then
170
172
  if ^X loves * then "We found "X" which is in love!"
171
- or "We found "X" which is stll single."
173
+ otherwise "We found "X" which is stll single."
172
174
 
173
175
  To combine several statements into one use a colon (":") with the final dot ("."):
174
176
 
@@ -186,38 +188,38 @@ or parentheses:
186
188
  There are other statements you can use:
187
189
 
188
190
  - "While":
189
-
191
+
190
192
  <pre><code>
191
- While &lt;fact expression&gt; [,] &lt;statement&gt;
193
+ While &lt;fact expression&gt; &lt;statement&gt;
192
194
  </code></pre>
193
-
195
+
194
196
  Here &lt;fact expression&gt; is the same as in `if` statement except that you may use `not` in top level:
195
197
 
196
198
  <pre><code>
197
199
  While X not loves *:
198
- If X is a boy Y is a girl then X loves Y.
200
+ If X is a boy and Y is a girl then X loves Y.
199
201
  </code></pre>
200
-
201
- The variables from &lt;fact expression&gt; are not available in &lt;statement&gt;
202
-
202
+
203
+ The variables from &lt;fact expression&gt; are not available in &lt;statement&gt;.
204
+
205
+ Note that usually there is no way to delimit &lt;fact expression&gt; from &lt;statement&gt; except by wrapping the &lt;statement&gt; into ":" and "." or into parentheses.
206
+
203
207
  - "Repeat n times":
204
-
208
+
205
209
  <pre><code>
206
210
  10 times "Hello!" (note: print "Hello!" 10 times)
207
- 10...20 times "Hello!" (note: random number between 10 and 20 is
208
- chosen)
209
- X times "Hello!" (note: the value of the captured variable X
210
- is used)
211
+ 10...20 times "Hello!" (note: random number between 10 and 20 is chosen)
212
+ X times "Hello!" (note: the value of the captured variable X is used)
211
213
  X...Y times "Hello!" (note: the value between two captured variables
212
214
  is used)
213
215
  </code></pre>
214
-
216
+
215
217
  - "For all":
216
-
218
+
217
219
  <pre><code>
218
- For all &lt;fact expression&gt; [,] &lt;statement&gt;
220
+ For all &lt;fact expression&gt; &lt;statement&gt;
219
221
  </code></pre>
220
-
222
+
221
223
  &lt;statement&gt; is executed for all combinations of variables in &lt;fact expression&gt;. The &lt;fact expression&gt; is like in `if` statement.
222
224
 
223
225
  - Ruby code:
@@ -227,21 +229,24 @@ There are other statements you can use:
227
229
  </code></pre>
228
230
 
229
231
  Inside the code you can access the captured variables by their lowercase names:
230
-
232
+
231
233
  <pre><code>
232
234
  If X loves Y then
233
235
  ```puts(x); puts(y)```
234
236
  </code></pre>
235
-
237
+
236
238
  - "Either ... or ...":
237
239
 
238
240
  <pre><code>
239
- either &lt;statement&gt; [,]
240
- or &lt;statement&gt; [,]
241
- or &lt;statement&gt;
241
+ Either &lt;statement&gt; [;|,]
242
+ or &lt;statement&gt; [;|,]
243
+ or &lt;statement&gt; [;|,]
242
244
  ...
245
+ or &lt;statement&gt; [;|,]
243
246
  </code></pre>
244
-
247
+
248
+ `[;|,]` means optional character which is either semicolon (";") or comma (",").
249
+
245
250
  A random &lt;statement&gt; is chosen and executed.
246
251
 
247
252
  ### Notes ###
@@ -250,43 +255,15 @@ There are other statements you can use:
250
255
 
251
256
  (note: this is a comment (with nested parentheses)!)
252
257
 
253
- Top-level statements may also be delimited with dot ("."):
254
-
255
- "John" is a boy.
256
- "Sam" is a boy.
257
- "Liza" is a girl.
258
- "Sabrina" is a girl.
259
- "John" loves "Liza".
260
-
261
- There is another form of the statements combination:
258
+ You may end the story with a dot ("."):
262
259
 
263
- if X is a boy then:
264
- - "We know "X;
265
- - X" is a good boy";
266
- - X" is glad to meet you".
260
+ "John" loves "Liza";
261
+ "Sam" loves "Sophia".
267
262
 
268
263
  Keywords `if`, `either`, `or`, `for` (in `for all`) and `while` may start with a capital letter: `If`, `Either` etc.
269
264
 
270
265
  You may also use russian keywords! ;)
271
266
 
272
- ### Mods ###
273
-
274
- When `-m` or `--allow-comma-in-facts` is passed to `story`, the optional comma in "while" and "for all" becomes disallowed:
275
-
276
- For all A, B, C loves D, "Love quadrangle!"; /* ERROR, ambiguity! */
277
- For all A, B, C loves D: "Love quadrangle!". /* OK */
278
- For all A, B, C loves D ("Love quadrangle!"); /* OK too */
279
-
280
- Trailing comma in Fact expression is ignored:
281
-
282
- For all X loves Y, ("Someone loves other one!")
283
- /* is the same as */
284
- For all X loves Y ("Someone loves other one!")
285
-
286
- If (A loves B,) and (B loves C,) then ...
287
- /* is the same as */
288
- If (A loves B) and (B loves C) then ...
289
-
290
267
  Examples
291
268
  --------
292
269
 
data/bin/story CHANGED
@@ -6,7 +6,6 @@ require 'story/samples_dir'
6
6
  $output_file = nil
7
7
  $input_file = nil
8
8
  $story_class_name = nil
9
- $allow_comma_in_facts = false
10
9
  $process = lambda do |story_class_code, output|
11
10
  # execute the story
12
11
  story_class = eval story_class_code
@@ -35,10 +34,6 @@ OptionParser.new do |opts|
35
34
  "instead of an anonymous class" do |name|
36
35
  $story_class_name = name
37
36
  end
38
- opts.on "-m", "--allow-comma-in-facts", "Allow `,' in Fact expressions",
39
- "(see `Mods' in README)" do
40
- $allow_comma_in_facts = true
41
- end
42
37
  opts.on "-r", "--show-relations", "Show relations mentioned in the story",
43
38
  "(useful for debug)" do
44
39
  $process = lambda do |story_class_code, output|
@@ -67,7 +62,7 @@ begin
67
62
  end
68
63
  story_class_code =
69
64
  begin
70
- Story.compile(input_text, $input_file || "-", $allow_comma_in_facts)
65
+ Story.compile(input_text, $input_file || "-")
71
66
  rescue Parse::Error => e
72
67
  abort "error: #{e.pos.file}:#{e.pos.line+1}:#{e.pos.column+1}: #{e.message}"
73
68
  end
data/lib/parse.rb CHANGED
@@ -17,6 +17,8 @@ class Parse
17
17
  #
18
18
  # parses +text+.
19
19
  #
20
+ # If $DEBUG == true then it prints {#rule} calls to stdout.
21
+ #
20
22
  # @param [String] text
21
23
  # @param [String] file file the +text+ is taken from.
22
24
  # @raise [Parse::Error, IOError]
@@ -32,7 +34,7 @@ class Parse
32
34
  if r.nil? or not @text.eos? then
33
35
  if @most_probable_error
34
36
  then raise @most_probable_error
35
- else raise Error.new(Position.new(@text.pos, @text), "syntax error")
37
+ else raise Error.new(Position.new(@text.pos, @text, @file), "syntax error")
36
38
  end
37
39
  end
38
40
  return r
@@ -195,7 +197,10 @@ class Parse
195
197
  old_rule_start_pos = @rule_start_pos
196
198
  @rule_start_pos = pos
197
199
  begin
198
- instance_eval(&body)
200
+ if $DEBUG then STDERR.puts("#{pos.file}:#{pos.line+1}:#{pos.column+1}: entering rule :#{name}"); end
201
+ r = instance_eval(&body)
202
+ if $DEBUG then STDERR.puts("#{pos.file}:#{pos.line+1}:#{pos.column+1}: exiting rule :#{name} #{if r.nil? then "(with nil)" end}"); end
203
+ r
199
204
  ensure
200
205
  @rule_start_pos = old_rule_start_pos
201
206
  end
data/lib/story/compile.rb CHANGED
@@ -21,16 +21,12 @@ class Story
21
21
 
22
22
  # @param [String] text
23
23
  # @param [String] file a file the +text+ is taken from.
24
- # @param [Boolean] allow_comma_in_fact_expr_primary
25
24
  # @return [String] a {String} which {Kernel#eval}s to a {Class} which
26
25
  # inherits {Story}.
27
26
  # @raise [Parse::Error]
28
- def self.compile(text, file = "-", allow_comma_in_fact_expr_primary = false)
27
+ def self.compile(text, file = "-")
29
28
  #
30
- c = Parse.new.
31
- tap { |p| p.allow_comma_in_fact_expr_primary = allow_comma_in_fact_expr_primary }.
32
- (text, file).
33
- to_code
29
+ c = Parse.new.(text, file).to_code
34
30
  #
35
31
  relation_id_to_var_arg_valuesss_var = Hash.new do |h, relation_id|
36
32
  h[relation_id] = "@#{INTERNAL_VAR_PREFIX}var_arg_valuesss#{h.size}"
@@ -336,7 +332,7 @@ class Story
336
332
 
337
333
  end
338
334
 
339
- Statement::Or = ASTNode.new :substatements do
335
+ Statement::Either = ASTNode.new :substatements do
340
336
 
341
337
  def to_code
342
338
  code << "[\n" <<
@@ -510,7 +506,7 @@ class Story
510
506
  rb_op =
511
507
  case e2.op
512
508
  when "==", "!=", "<=", ">=", "<", ">" then e2.op
513
- when "<>" then "!="
509
+ when "<>", "=/=" then "!="
514
510
  when "=" then "=="
515
511
  end
516
512
  with_var_args(
@@ -598,14 +594,6 @@ class Story
598
594
  # @!visibility private
599
595
  class Parse < ::Parse
600
596
 
601
- # ---------------
602
- # @!group Options
603
- # ---------------
604
-
605
- # @return [Boolean] is "," in {#fact_expr_primary} allowed? Default is
606
- # false.
607
- attr_accessor :allow_comma_in_fact_expr_primary
608
-
609
597
  # --------------
610
598
  # @!group Syntax
611
599
  # --------------
@@ -613,13 +601,13 @@ class Story
613
601
  rule :start do
614
602
  no_errors { wsc } and
615
603
  ss = many {
616
- s = statement and opt { _{ semicolon } or _{ dot } } and s
604
+ s = statement and opt{semicolon} and s
617
605
  } and
606
+ opt{dot} and
618
607
  Program[ss]
619
608
  end
620
609
 
621
610
  rule :statement do
622
- _{ statement_or } or
623
611
  _{ statement_if } or
624
612
  _{ statement_while } or
625
613
  _{ statement_for_all } or
@@ -627,14 +615,28 @@ class Story
627
615
  _{ statement_code } or
628
616
  _{ statement_set_fact } or
629
617
  _{ statement_tell } or
630
- _{ statement_compound }
618
+ _{ statement_compound } or
619
+ _{ statement_either }
631
620
  end
632
621
 
633
- rule :statement_tell do
634
- parts = many1 {
635
- _{ string } or _{ newline } or _{ var }
622
+ rule :statement_either do
623
+ either and s1 = statement and opt_c_or_s and sn = many {
624
+ _or_ and s = statement and opt_c_or_s and s
636
625
  } and
637
- _(Statement::Tell[parts])
626
+ if sn.empty?
627
+ then s1
628
+ else _(Statement::Either[[s1, *sn]])
629
+ end
630
+ end
631
+
632
+ def opt_c_or_s
633
+ opt{ _{semicolon} or _{comma} }
634
+ end
635
+
636
+ rule :statement_tell do
637
+ expect("\"smth\"") { no_errors {
638
+ e = fact_expr_primary_or_statement_tell and e.is_a?(Statement::Tell) and e
639
+ } }
638
640
  end
639
641
 
640
642
  rule :statement_set_fact do
@@ -642,44 +644,41 @@ class Story
642
644
  end
643
645
 
644
646
  rule :statement_if do
645
- if_then = lambda {
646
- _if_ and c = fact_expr and opt { comma } and _then_ and s = statement and [c, s]
647
- }
648
- or_delimiter = lambda {
649
- opt { comma } and _or_
647
+ # single `then' form
648
+ _{
649
+ _if_ and c = fact_expr and opt{comma} and _then_ and s1 = statement and opt{semicolon} and
650
+ s2 = opt { otherwise and s = statement and opt{semicolon} and s } and
651
+ _(Statement::If[ [[c, s1]], s2.first ])
652
+ } or
653
+ # multiple `then' form
654
+ _{
655
+ _if_ and colon and c_and_sn = many {
656
+ (_{dash} or _{ word and rparen }) and c = fact_expr and opt{comma} and _then_ and s = statement and opt{semicolon} and [c, s]
657
+ } and
658
+ otherwise_s = opt {
659
+ otherwise and s = statement and opt{semicolon} and s
660
+ } and
661
+ _(Statement::If[c_and_sn, otherwise_s.first])
650
662
  }
651
- #
652
- c_and_s1 = if_then.() and
653
- c_and_sn = many { or_delimiter.() and if_then.() } and
654
- else_a = opt { or_delimiter.() and statement } and
655
- _(Statement::If[[c_and_s1, *c_and_sn], else_a.first])
656
663
  end
657
664
 
658
665
  rule :statement_while do
659
- _while_ and f = fact_expr and opt { comma } and s = statement and
666
+ _while_ and f = fact_expr and s = statement and
660
667
  _(Statement::While[f, s])
661
668
  end
662
669
 
663
670
  rule :statement_for_all do
664
- _for_ and all and f = fact_expr and opt { comma } and s = statement and
671
+ _for_ and all and f = fact_expr and s = statement and
665
672
  _(Statement::ForAll[f, s])
666
673
  end
667
674
 
668
675
  rule :statement_n_times do
669
- val = lambda {
670
- _{ number } or _{ var }
671
- }
676
+ val = lambda { _{number} or _{var} or _{above_var} }
672
677
  #
673
678
  range_begin = range_end = val.() and opt { ellipsis and range_end = val.() } and times and s = statement and
674
679
  _(Statement::NTimes[range_begin, range_end, s])
675
680
  end
676
681
 
677
- rule :statement_or do
678
- _or_ and s1 = statement and
679
- ss = many { opt { comma } and _or_ and statement } and
680
- Statement::Or[[s1, *ss]]
681
- end
682
-
683
682
  rule :statement_code do
684
683
  c = _code_ and
685
684
  act {
@@ -695,7 +694,7 @@ class Story
695
694
  rule :statement_compound do
696
695
  body = lambda {
697
696
  ss = many {
698
- opt { dash } and s = statement and opt { semicolon } and s
697
+ s = statement and opt{semicolon} and s
699
698
  } and
700
699
  _(Statement::Compound[ss])
701
700
  }
@@ -737,37 +736,50 @@ class Story
737
736
 
738
737
  rule :fact_expr_select do
739
738
  #
740
- operand = lambda { _{ var } or _{ value } or _{ above_var } }
739
+ operand = lambda { _{var} or _{value} or _{above_var} }
741
740
  #
742
741
  v1 = operand.() and op = comparison_op and v2 = operand.() and
743
742
  _(FactExpr::Select[v1, op, v2])
744
743
  end
745
744
 
746
745
  rule :fact_expr_primary do
747
- allow_comma = allow_comma_in_fact_expr_primary
746
+ expect("Fact expression") { no_errors {
747
+ f = fact_expr_primary_or_statement_tell and f.is_a?(FactExpr) and f
748
+ } }
749
+ end
750
+
751
+ rule :fact_expr_primary_or_statement_tell do
748
752
  relation_id = []
749
753
  args = []
750
754
  negated = false
751
755
  #
752
756
  one_or_more {
753
- _{ s = (_{dash} or _{other_char}) and act { relation_id << s } } or
754
- _{ allow_comma and s = comma and act { relation_id << s } } or
757
+ _{ s = (_{dash} or _{other_char} or _{comma}) and act { relation_id << s } } or
755
758
  _{ a = asterisk and act { args << a; relation_id << :* } } or
756
759
  _{ _not_ and act { negated = !negated } } or
757
- _{ v = value and act { args << v; relation_id << :* } } or
758
- _{ v = var and act { args << v; relation_id << :* } } or
759
- _{ v = above_var and act { args << v; relation_id << :* } } or
760
- _{ w = word and act { relation_id << w.ru_downcase } }
760
+ _{ v = (_{value} or _{var} or _{above_var}) and act { args << v; relation_id << :* } } or
761
+ _{
762
+ w = (
763
+ _{ word } or
764
+ _{ _for_ and not_follows {all} } or
765
+ _{ all }
766
+ ) and
767
+ act { relation_id << w.ru_downcase }
768
+ }
761
769
  } and
762
770
  act { relation_id.chomp!(",") } and
763
- # this is not a "statement_tell" and
764
- not relation_id.all? { |p| p == :* } and
765
- e = _(FactExpr::Primary[relation_id, args]) and
766
- if negated then _(FactExpr::Not[e]); else e; end
771
+ if not negated and relation_id.all? { |p| p == :* } then
772
+ _(Statement::Tell[args])
773
+ else
774
+ e = _(FactExpr::Primary[relation_id, args])
775
+ if negated then e = _(FactExpr::Not[e]); end
776
+ e
777
+ end
767
778
  end
768
779
 
769
780
  rule :value do
770
781
  _{ string } or
782
+ _{ nl } or
771
783
  _{ number }
772
784
  end
773
785
 
@@ -779,6 +791,10 @@ class Story
779
791
  # @!group Lexical Analysis
780
792
  # ------------------------
781
793
 
794
+ begin
795
+ @@keyword_method_ids = []
796
+ end
797
+
782
798
  # macro
783
799
  def self.token(method_id, human_readable_description, &body)
784
800
  rule method_id do
@@ -797,6 +813,15 @@ class Story
797
813
  end
798
814
  end
799
815
 
816
+ # macro
817
+ def self.keyword(method_id, human_readable_description, check_regexp, &return_value_f)
818
+ return_value_f ||= lambda { |captured_string| captured_string }
819
+ @@keyword_method_ids.push(method_id)
820
+ token method_id, human_readable_description do
821
+ t = word_keyword_or_var and check_regexp === t and return_value_f.(t)
822
+ end
823
+ end
824
+
800
825
  simple_token :lparen, "("
801
826
  simple_token :rparen, ")"
802
827
  simple_token :comma, ","
@@ -806,60 +831,34 @@ class Story
806
831
  simple_token :dash, "-"
807
832
  simple_token :ellipsis, "..."
808
833
 
834
+ keyword :_for_, "`for'", /^([Ff]or|[Дд]ля)$/
835
+ keyword :all, "`all'", /^(all|всех)$/
836
+ keyword :_while_, "`while'", /^([Ww]hile|[Пп]ока)$/
837
+ keyword :_if_, "`if'", /^([Ii]f|[Ее]сли)$/
838
+ keyword :_then_, "`then'", /^(then|то)$/
839
+ keyword :otherwise, "`otherwise'", /^([Oo]therwise|[Ии]наче)$/
840
+ keyword :_not_, "`not'", /^(not|не)$/
841
+ keyword :either, "`either'", /^([Ee]ither|[Ии]ли|[Лл]ибо)$/
842
+ keyword :_or_, "`or'", /^([Oo]r|[Ии]ли|[Лл]ибо)$/
843
+ keyword :_and_, "`and'", /^(and|и)$/
844
+ keyword :times, "`times'", /^(times|раза?)$/
845
+ keyword :newline, "`newline'", /^(newline|nl)$/, &proc { "\n" }
846
+ alias nl newline
847
+
809
848
  token :comparison_op, "comparison operator" do
810
- scan(/=|!=|<>|<=|>=|<|>/)
849
+ scan(/=\/=|=|!=|<>|<=|>=|<|>/)
811
850
  end
812
851
 
813
852
  token :asterisk, "`*'" do
814
853
  scan("*") and _(Asterisk.new)
815
854
  end
816
855
 
817
- token :other_char, "any of `#№@$%/'" do
818
- scan(/[\#\№\@\$\%\/]/)
819
- end
820
-
821
- token :_for_, "`for'" do
822
- t = word_ and /^([Ff]or|[Дд]ля)$/ === t
823
- end
824
-
825
- token :all, "`all'" do
826
- t = word_ and /^(all|всех)$/ === t
827
- end
828
-
829
- token :_while_, "`while'" do
830
- t = word_ and /^([Ww]hile|[Пп]ока)$/ === t
831
- end
832
-
833
- token :_then_, "`then'" do
834
- t = word_ and /^(then|то)$/ === t
835
- end
836
-
837
- token :_if_, "`if'" do
838
- t = word_ and /^([Ii]f|[Ее]сли)$/ === t
839
- end
840
-
841
- token :_not_, "`not'" do
842
- t = word_ and /^(not|не)$/ === t
843
- end
844
-
845
- token :_or_, "`or'" do
846
- t = word_ and /^([Ee]ither|[Oo]r|[Ии]ли|[Лл]ибо)$/ === t
847
- end
848
-
849
- token :_and_, "`and'" do
850
- t = word_ and /^(and|и)$/ === t
851
- end
852
-
853
- token :times, "`times'" do
854
- t = word_ and /^(times|раза?)$/ === t
855
- end
856
-
857
- token :newline, "`newline'" do
858
- t = word_ and /^(newline|nl)$/ === t and "\n"
856
+ token :other_char, "any of `#№@$%/[]{}'" do
857
+ scan(/[\#\№\@\$\%\/\[\]\{\}]/)
859
858
  end
860
859
 
861
860
  token :number, "number" do
862
- n = scan(/\d+/) and n.to_i
861
+ n = scan(/-?\d+/) and Integer(n)
863
862
  end
864
863
 
865
864
  token :string, "string" do
@@ -884,7 +883,7 @@ class Story
884
883
  end
885
884
 
886
885
  token :var, "variable" do
887
- n = word_ and
886
+ n = word_keyword_or_var and
888
887
  /^[_A-ZА-ЯЁ][_A-ZА-ЯЁ0-9]*$/ === n and
889
888
  _(Var[n.ru_downcase, n])
890
889
  end
@@ -894,14 +893,10 @@ class Story
894
893
  end
895
894
 
896
895
  token :word, "word" do
897
- not_follows(
898
- :_not_, :_and_, :_or_, :_if_, :_then_, :var, :_while_, :_for_, :all,
899
- :times, :newline
900
- ) and
901
- word_
896
+ not_follows(:var, *@@keyword_method_ids) and word_keyword_or_var
902
897
  end
903
898
 
904
- rule :word_ do
899
+ rule :word_keyword_or_var do
905
900
  scan(/[a-zA-Zа-яёА-ЯЁ_](['\-]?[a-zA-Zа-яёА-ЯЁ0-9_]+)*/)
906
901
  end
907
902
 
@@ -949,16 +944,3 @@ class Story
949
944
  end
950
945
 
951
946
  end
952
-
953
- # begin
954
- # eval(Story.compile(<<-STORY)).new.write
955
- #
956
- # "Hello, world!" newline
957
- # Either "John" loves "Liza",
958
- # or ("Hello, world, again!" nl; "John" kisses "Liza";).
959
- # STORY
960
- # rescue Parse::Error => e
961
- # puts "error: #{e.pos.file}:#{e.pos.line}:#{e.pos.column}: #{e.message}"
962
- # # rescue Story::Error => e
963
- # # puts "error: #{e.pos.file}:#{e.pos.line}:#{e.pos.column}: #{e.message}"
964
- # end
@@ -20,13 +20,13 @@ names = UniqueNames.english_male
20
20
  "- "N", the ";
21
21
  either "tiger", or "fox", or "bear", or ("horse"; N has hooves);
22
22
  " ";
23
- either ("with powerful hands"; N uses "hands"),
24
- or ("with powerful feet"; N uses "feet"),
23
+ either ("with powerful hands"; N uses "hands");
24
+ or ("with powerful feet"; N uses "feet");
25
25
  or:
26
26
  if ^N has hooves then (
27
27
  "with powerful hooves"; N uses "hooves"
28
28
  )
29
- or (
29
+ otherwise (
30
30
  "with sharp claws"; N uses "claws"
31
31
  )
32
32
  .
@@ -38,20 +38,21 @@ nl
38
38
  nl
39
39
 
40
40
  While the tournament not complete:
41
- If X is in and Y is in and X != Y, then:
41
+ If X is in and Y is in and X != Y then:
42
42
  (note: fight!)
43
- either (X won; Y lost), or (Y won; X lost);
43
+ either (X won; Y lost) or (Y won; X lost);
44
44
  if A won and B lost then:
45
- if ^A uses "hands" then ""A" has beaten "B". "
46
- or if ^A uses "feet" then ""A" has kicked "B". "
47
- or if ^A uses "hooves" then ""A" has kicked "B" and knocked him out. "
48
- or if ^A uses "claws" then ""A" has scratched the whole "B". ";
45
+ if:
46
+ - ^A uses "hands" then ""A" has beaten "B". ";
47
+ - ^A uses "feet" then ""A" has kicked "B". ";
48
+ - ^A uses "hooves" then ""A" has kicked "B" and knocked him out. ";
49
+ - ^A uses "claws" then ""A" has scratched the whole "B". ";
49
50
  A not won; B not lost;
50
51
  B is not in;
51
52
  .
52
53
  .
53
- Or the tournament complete;
54
+ Otherwise the tournament complete;
54
55
  .
55
56
  If X is in then ""X" is the winner!"
56
57
  nl
57
- nl
58
+ nl
@@ -1,15 +1,15 @@
1
1
  (note: this is a sample story written in SDL - Story Description Language)
2
2
 
3
- "Oliver" is a boy.
4
- "Sam" is a boy.
5
- "Nathan" is a boy.
6
- "John" is a boy.
7
- "Jack" is a boy.
8
- "Katie" is a girl.
9
- "Abigail" is a girl.
10
- "Rose" is a girl.
11
- "Madeleine" is a girl.
12
- "Liza" is a girl.
3
+ "Oliver" is a boy;
4
+ "Sam" is a boy;
5
+ "Nathan" is a boy;
6
+ "John" is a boy;
7
+ "Jack" is a boy;
8
+ "Katie" is a girl;
9
+ "Abigail" is a girl;
10
+ "Rose" is a girl;
11
+ "Madeleine" is a girl;
12
+ "Liza" is a girl;
13
13
 
14
14
  "
15
15
  University
@@ -17,8 +17,7 @@ University
17
17
 
18
18
  "
19
19
 
20
- "Once upon a time in the University went "; for all X is a boy or X is a girl,
21
- X", "; "that's it. They quickly became friends and went out often. "
20
+ "Once upon a time in the University went "; for all X is a boy or X is a girl (X", "); "that's it. They quickly became friends and went out often. "
22
21
 
23
22
  While X is a boy and X not loves *:
24
23
  if X is a boy and X not loves * and Y is a girl and * not loves Y then:
@@ -53,4 +52,4 @@ nl
53
52
  nl
54
53
  "Then they all lived long and happy!"
55
54
  nl
56
- nl
55
+ nl
@@ -26,7 +26,7 @@ names = UniqueNames.russian_male
26
26
  если ^N имеет копыта, то (
27
27
  "с мощными копытами"; N использует "копыта"
28
28
  )
29
- или (
29
+ иначе (
30
30
  "с острыми когтями"; N использует "когти"
31
31
  )
32
32
  .
@@ -42,15 +42,16 @@ nl
42
42
  (note: бой!)
43
43
  либо (X победил; Y проиграл), либо (Y победил; X проиграл);
44
44
  если A победил и B проиграл, то:
45
- если ^A использует "руки", то ""A" избил "B". "
46
- или если ^A использует "ноги", то ""A" запинал "B". "
47
- или если ^A использует "копыта", то ""A" лягнул "B" и вырубил его. "
48
- или если ^A использует "когти", то ""A" исцарапал всего "B". ";
45
+ если:
46
+ - ^A использует "руки", то ""A" избил "B". ";
47
+ - ^A использует "ноги", то ""A" запинал "B". ";
48
+ - ^A использует "копыта", то ""A" лягнул "B" и вырубил его. ";
49
+ - ^A использует "когти", то ""A" исцарапал всего "B". ";
49
50
  A не победил; B не проиграл;
50
- A не участвует в турнире;
51
+ B не участвует в турнире;
51
52
  .
52
53
  .
53
- Или конец турнира;
54
+ Иначе конец турнира;
54
55
  .
55
56
  Если X участвует в турнире, то ""X" - победитель!"
56
57
  nl
@@ -1,15 +1,15 @@
1
1
  (прим.: это пример стории, написанной на SDL - Языке Описания Историй)
2
2
 
3
- "Слава" - самец.
4
- "Вова" - самец.
5
- "Юрий" - самец.
6
- "Борис" - самец.
7
- "Гриша" - самец.
8
- "Зоя" - самка.
9
- "Вика" - самка.
10
- "Света" - самка.
11
- "Лиля" - самка.
12
- "Юля" - самка.
3
+ "Слава" - самец;
4
+ "Вова" - самец;
5
+ "Юрий" - самец;
6
+ "Борис" - самец;
7
+ "Гриша" - самец;
8
+ "Зоя" - самка;
9
+ "Вика" - самка;
10
+ "Света" - самка;
11
+ "Лиля" - самка;
12
+ "Юля" - самка;
13
13
 
14
14
  "
15
15
  Университет
@@ -17,7 +17,7 @@
17
17
 
18
18
  "
19
19
 
20
- "Как-то раз в университет поступили "; для всех X - самец или X - самка, X", ";
20
+ "Как-то раз в университет поступили "; для всех X - самец или X - самка (X", ");
21
21
  "вот. Они быстро стали друзьями и часто гуляли все вместе. "
22
22
 
23
23
  Пока X - самец и X не любит *:
@@ -54,4 +54,4 @@ nl
54
54
  nl
55
55
  "И жили они долго и счастливо!"
56
56
  nl
57
- nl
57
+ nl
@@ -1,15 +1,15 @@
1
1
  (прим.: это пример стории, написанной на SDL - Языке Описания Историй)
2
2
 
3
- "Слава" - самец.
4
- "Вова" - самец.
5
- "Юрий" - самец.
6
- "Борис" - самец.
7
- "Гриша" - самец.
8
- "Зоя" - самка.
9
- "Вика" - самка.
10
- "Света" - самка.
11
- "Лиля" - самка.
12
- "Юля" - самка.
3
+ "Слава" - самец;
4
+ "Вова" - самец;
5
+ "Юрий" - самец;
6
+ "Борис" - самец;
7
+ "Гриша" - самец;
8
+ "Зоя" - самка;
9
+ "Вика" - самка;
10
+ "Света" - самка;
11
+ "Лиля" - самка;
12
+ "Юля" - самка;
13
13
 
14
14
  "
15
15
  Университет
@@ -17,7 +17,7 @@
17
17
  "
18
18
  nl
19
19
 
20
- "Как-то раз в университет поступили "; для всех X - самец или X - самка, X", ";
20
+ "Как-то раз в университет поступили "; для всех X - самец или X - самка (X", ");
21
21
  "вот. Они быстро стали друзьями и часто гуляли все вместе. "
22
22
 
23
23
  Для всех X - самец или X - самка: X свободен.
@@ -66,6 +66,6 @@ nl
66
66
 
67
67
  nl
68
68
  nl
69
- "И жили они долго и счастливо!". Если X любит Y и ((X - самец и Y - самец) или (X - самка и Y - самка)), то " Правда, роскомнадзорненько как-то получилось.";
69
+ "И жили они долго и счастливо!"; если X любит Y и ((X - самец и Y - самец) или (X - самка и Y - самка)), то " Правда, роскомнадзорненько как-то получилось.";
70
+ nl
70
71
  nl
71
- nl
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: story-gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-02-04 00:00:00.000000000 Z
12
+ date: 2016-02-17 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Generate stories from descriptions based on Facts!
15
15
  email: various.furriness@gmail.com
@@ -43,6 +43,7 @@ files:
43
43
  - README.md
44
44
  - .yardopts
45
45
  - yardopts_extra.rb
46
+ - NEWS
46
47
  - sample/fight_club.sdl
47
48
  - sample/университет.sdl
48
49
  - sample/бойцовский_клуб.sdl