skeem 0.2.15 → 0.2.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +451 -195
  3. data/.travis.yml +27 -0
  4. data/CHANGELOG.md +26 -0
  5. data/Gemfile +2 -0
  6. data/README.md +3 -2
  7. data/Rakefile +2 -0
  8. data/appveyor.yml +3 -4
  9. data/bin/skeem +15 -15
  10. data/lib/skeem/datum_dsl.rb +40 -30
  11. data/lib/skeem/element_visitor.rb +5 -2
  12. data/lib/skeem/grammar.rb +77 -54
  13. data/lib/skeem/interpreter.rb +9 -7
  14. data/lib/skeem/parser.rb +6 -4
  15. data/lib/skeem/primitive/primitive_builder.rb +130 -122
  16. data/lib/skeem/primitive/primitive_procedure.rb +23 -25
  17. data/lib/skeem/runtime.rb +17 -15
  18. data/lib/skeem/s_expr_builder.rb +39 -147
  19. data/lib/skeem/s_expr_nodes.rb +147 -132
  20. data/lib/skeem/skeem_exception.rb +1 -0
  21. data/lib/skeem/skm_binding.rb +9 -11
  22. data/lib/skeem/skm_compound_datum.rb +9 -6
  23. data/lib/skeem/skm_element.rb +15 -13
  24. data/lib/skeem/skm_empty_list.rb +6 -4
  25. data/lib/skeem/skm_exception.rb +9 -0
  26. data/lib/skeem/skm_expression.rb +3 -1
  27. data/lib/skeem/skm_frame.rb +3 -2
  28. data/lib/skeem/skm_pair.rb +26 -18
  29. data/lib/skeem/skm_procedure_exec.rb +11 -6
  30. data/lib/skeem/skm_simple_datum.rb +23 -20
  31. data/lib/skeem/skm_unary_expression.rb +34 -37
  32. data/lib/skeem/tokenizer.rb +40 -30
  33. data/lib/skeem/version.rb +3 -1
  34. data/lib/skeem.rb +2 -0
  35. data/skeem.gemspec +7 -5
  36. data/spec/skeem/add4.skm +4 -0
  37. data/spec/skeem/datum_dsl_spec.rb +13 -12
  38. data/spec/skeem/element_visitor_spec.rb +14 -10
  39. data/spec/skeem/interpreter_spec.rb +76 -46
  40. data/spec/skeem/lambda_spec.rb +13 -11
  41. data/spec/skeem/parser_spec.rb +23 -19
  42. data/spec/skeem/primitive/primitive_builder_spec.rb +55 -46
  43. data/spec/skeem/primitive/primitive_procedure_spec.rb +14 -12
  44. data/spec/skeem/runtime_spec.rb +20 -18
  45. data/spec/skeem/s_expr_nodes_spec.rb +8 -6
  46. data/spec/skeem/skm_compound_datum_spec.rb +12 -10
  47. data/spec/skeem/skm_element_spec.rb +7 -5
  48. data/spec/skeem/skm_empty_list_spec.rb +7 -5
  49. data/spec/skeem/skm_frame_spec.rb +5 -4
  50. data/spec/skeem/skm_pair_spec.rb +9 -8
  51. data/spec/skeem/skm_procedure_exec_spec.rb +2 -0
  52. data/spec/skeem/skm_simple_datum_spec.rb +24 -22
  53. data/spec/skeem/skm_unary_expression_spec.rb +11 -9
  54. data/spec/skeem/tokenizer_spec.rb +54 -43
  55. data/spec/skeem_spec.rb +2 -0
  56. data/spec/spec_helper.rb +15 -10
  57. metadata +13 -9
data/.travis.yml ADDED
@@ -0,0 +1,27 @@
1
+ language: ruby
2
+ dist: trusty
3
+
4
+ before_install:
5
+ - gem update --system
6
+ - gem install bundler
7
+
8
+ script:
9
+ - bundle exec rake
10
+
11
+ rvm:
12
+ - 2.6.3
13
+ - 2.5.5
14
+ - 2.4.6
15
+ - 2.3.8
16
+ - jruby-9.1.9.0
17
+
18
+ matrix:
19
+ allow_failures:
20
+ - rvm: ruby-head
21
+ - rvm: jruby-head
22
+
23
+
24
+ # whitelist
25
+ branches:
26
+ only:
27
+ - master
data/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ ## [0.2.19] - 2021-08-30
2
+ - Grammar refactoring: use ?, * and + modifiers in Rley 0.8.03 rules
3
+
4
+ ### Changed
5
+ - File `grammar.rb`: Remove redundant rules after introduction og ?, * and + modifiers
6
+ - Class `SkmBuilder`: Removal of useless methods after removal of their rule counterpart
7
+
8
+
9
+
10
+ ## [0.2.18] - 2021-08-29
11
+ - Minor refactoring for Rley 0.8.03
12
+ - Code restyling to please rubocop 1.19.1.
13
+
14
+ ### Changed
15
+ - File `.rubocop.yml` updated with newer Rubocop cops
16
+ - File `grammar.rb`: Calling `Rley::grammar_builder`, replace `LET*` token by `LET_STAR`
17
+ - Other files changed to get rid of Rubocop offences
18
+
19
+ ## [0.2.17] - 2020-05-09
20
+ - Updated `.rubocop.yml` file.
21
+ - Code restyling to please rubocop 0.8.0.
22
+
23
+ ## [0.2.16] - 2019-08-18
24
+ - Code refactoring to use string frozen magic comments (as a consequence, srl_ruby runs only on Rubies 2.3 or newer).
25
+ - Code restyling to please rubocop 0.7.40.
26
+
1
27
  ## [0.2.15] - 2019-07-02
2
28
  - Added a basic command-line and console interface for Skeem interpreter.
3
29
  - Added procedures: `display!`, `newline`, `make-list`
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
data/README.md CHANGED
@@ -32,7 +32,8 @@ If you're not familiar to Scheme, the section [About Scheme](#about-scheme) cont
32
32
 
33
33
  ## Usage
34
34
  Once the gem is installed, the `skeem` executable can be used.
35
- It allows to run the interpreter from the command line.
35
+ It allows to run the interpreter from a REPL console or from the command line.
36
+ Another way to interact with the Skeem interpreter is to embed it in your Ruby code.
36
37
 
37
38
  ### Launching a REPL session
38
39
  To start a REPL (Read-Eval-Print-Loop) session, just type:
@@ -42,7 +43,7 @@ To start a REPL (Read-Eval-Print-Loop) session, just type:
42
43
 
43
44
  Skeem displays a greeting, a prompt and then waits for your input:
44
45
  ```
45
- Welcome to Skeem 0.2.15.
46
+ Welcome to Skeem 0.2.16.
46
47
  >
47
48
  ```
48
49
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
 
data/appveyor.yml CHANGED
@@ -1,17 +1,16 @@
1
1
  version: '{build}'
2
- max_jobs: 3
2
+ max_jobs: 5
3
3
  environment:
4
4
  matrix:
5
+ - Ruby_version: 26-x64
5
6
  - Ruby_version: 25-x64
6
7
  - Ruby_version: 24-x64
7
8
  - Ruby_version: 23-x64
9
+ - Ruby_version: 26
8
10
  - Ruby_version: 25
9
11
  - Ruby_version: 24
10
12
  - Ruby_version: 23
11
13
 
12
- # These are failing
13
- # - Ruby_version: 26
14
- # - Ruby_version: 26-x64
15
14
 
16
15
  install:
17
16
  - set PATH=C:\Ruby%Ruby_version%\bin;%PATH%
data/bin/skeem CHANGED
@@ -1,20 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'skeem'
4
5
 
5
6
  class SkeemREPL
6
7
  attr_reader :interpreter
7
8
 
8
- def initialize()
9
+ def initialize
9
10
  @interpreter = Skeem::Interpreter.new
10
11
  end
11
12
 
12
13
  def run_session
13
- puts "Welcome to Skeem #{Skeem::VERSION}."
14
+ puts "Welcome to Skeem #{Skeem::VERSION}."
14
15
  loop do
15
16
  print '> '
16
17
  input = gets.chomp.strip
17
- break if input == '(quit)' || input == '(exit)'
18
+ break if %w[(quit) (exit)].include?(input)
19
+
18
20
  process_input(input)
19
21
  end
20
22
  end
@@ -22,15 +24,13 @@ class SkeemREPL
22
24
  def run_file(aFilename)
23
25
  if File.exist?(aFilename)
24
26
  valid_fname = aFilename
25
- else # Retry by adding extension...
26
- if File.extname(aFilename).empty?
27
- if File.exist?(aFilename + '.skm')
28
- valid_fname = aFilename + '.skm'
29
- elsif File.exist?(aFilename + '.scm')
30
- valid_fname = aFilename + '.scm'
31
- else
32
- valid_fname = nil
33
- end
27
+ elsif File.extname(aFilename).empty? # Retry by adding extension...
28
+ if File.exist?("#{aFilename}.skm")
29
+ valid_fname = "#{aFilename}.skm"
30
+ elsif File.exist?("#{aFilename}.scm")
31
+ valid_fname = "#{aFilename}.scm"
32
+ else
33
+ valid_fname = nil
34
34
  end
35
35
  end
36
36
 
@@ -43,8 +43,8 @@ class SkeemREPL
43
43
  def process_input(input)
44
44
  begin
45
45
  ptree = interpreter.parse(input)
46
- rescue StandardError => exc
47
- $stderr.puts exc.message
46
+ rescue StandardError => e
47
+ $stderr.puts e.message
48
48
  return
49
49
  end
50
50
 
@@ -69,4 +69,4 @@ if ARGV.empty?
69
69
  djinn.run_session
70
70
  else
71
71
  djinn.run_file(ARGV[0])
72
- end
72
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'skm_simple_datum'
2
4
  require_relative 'skm_compound_datum'
3
5
  require_relative 'skm_pair'
@@ -9,7 +11,7 @@ module Skeem
9
11
  def boolean(aBoolean)
10
12
  return aBoolean if aBoolean.kind_of?(SkmBoolean)
11
13
 
12
- result = case aBoolean
14
+ case aBoolean
13
15
  when TrueClass, FalseClass
14
16
  SkmBoolean.create(aBoolean)
15
17
  when /^#t(?:rue)?|true$/
@@ -24,7 +26,7 @@ module Skeem
24
26
  def integer(aLiteral)
25
27
  return aLiteral if aLiteral.kind_of?(SkmInteger)
26
28
 
27
- result = case aLiteral
29
+ case aLiteral
28
30
  when Integer
29
31
  SkmInteger.create(aLiteral)
30
32
  when /^[+-]?\d+$/
@@ -37,7 +39,7 @@ module Skeem
37
39
  def rational(aLiteral)
38
40
  return aLiteral if aLiteral.kind_of?(SkmRational)
39
41
 
40
- result = case aLiteral
42
+ case aLiteral
41
43
  when Rational
42
44
  SkmRational.create(aLiteral)
43
45
  when /^[+-]?\d+\/\d+$/
@@ -50,7 +52,7 @@ module Skeem
50
52
  def real(aLiteral)
51
53
  return aLiteral if aLiteral.kind_of?(SkmReal)
52
54
 
53
- result = case aLiteral
55
+ case aLiteral
54
56
  when Numeric
55
57
  SkmReal.create(aLiteral)
56
58
  when /^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?$/
@@ -63,7 +65,7 @@ module Skeem
63
65
  def char(aLiteral)
64
66
  return aLiteral if aLiteral.kind_of?(SkmChar)
65
67
 
66
- result = case aLiteral
68
+ case aLiteral
67
69
  when Numeric
68
70
  SkmChar.create_from_int(aLiteral)
69
71
  when String
@@ -80,7 +82,7 @@ module Skeem
80
82
  def string(aLiteral)
81
83
  return aLiteral if aLiteral.kind_of?(SkmString)
82
84
 
83
- result = case aLiteral
85
+ case aLiteral
84
86
  when String
85
87
  SkmString.create(aLiteral)
86
88
  when SkmIdentifier
@@ -93,7 +95,7 @@ module Skeem
93
95
  def identifier(aLiteral)
94
96
  return aLiteral if aLiteral.kind_of?(SkmIdentifier)
95
97
 
96
- result = case aLiteral
98
+ case aLiteral
97
99
  when String
98
100
  SkmIdentifier.create(aLiteral)
99
101
  when SkmString
@@ -106,7 +108,7 @@ module Skeem
106
108
  alias symbol identifier
107
109
 
108
110
  def list(aLiteral)
109
- result = case aLiteral
111
+ case aLiteral
110
112
  when Array
111
113
  SkmPair.create_from_a(to_datum(aLiteral))
112
114
  when SkmPair
@@ -114,12 +116,10 @@ module Skeem
114
116
  else
115
117
  SkmPair.new(to_datum(aLiteral), SkmEmptyList.instance)
116
118
  end
117
-
118
- result
119
119
  end
120
120
 
121
121
  def vector(aLiteral)
122
- result = case aLiteral
122
+ case aLiteral
123
123
  when Array
124
124
  SkmVector.new(to_datum(aLiteral))
125
125
  when SkmVector
@@ -127,8 +127,6 @@ module Skeem
127
127
  else
128
128
  SkmVector.new([to_datum(aLiteral)])
129
129
  end
130
-
131
- result
132
130
  end
133
131
 
134
132
  # Conversion from Ruby object value to Skeem datum
@@ -137,7 +135,7 @@ module Skeem
137
135
  return vector(aLiteral.members) if aLiteral.kind_of?(SkmVector)
138
136
  return aLiteral if aLiteral.kind_of?(Primitive::PrimitiveProcedure)
139
137
 
140
- result = case aLiteral
138
+ case aLiteral
141
139
  when Array
142
140
  aLiteral.map { |elem| to_datum(elem) }
143
141
  when Integer
@@ -150,33 +148,45 @@ module Skeem
150
148
  SkmBoolean.create(aLiteral)
151
149
  when String
152
150
  parse_literal(aLiteral)
153
- when SkmPair # Special case: not a PORO literal
151
+ when SkmPair # Special case: not a PORO literal
154
152
  # One assumes that a Skeem list contains only Skeem datum objects
155
153
  SkmPair.create_from_a(aLiteral.to_a)
156
154
  when SkmUndefined
157
155
  aLiteral
158
156
  else
159
157
  raise StandardError, aLiteral.inspect
160
- end
158
+ end
161
159
  end
162
160
 
163
161
  private
164
162
 
165
163
  def parse_literal(aLiteral)
166
- if aLiteral =~ /^#t(?:rue)?|true$/
167
- boolean(aLiteral)
168
- elsif aLiteral =~ /^#f(?:alse)?|false$/
169
- boolean(aLiteral)
170
- elsif aLiteral =~ /^[+-]?\d+\/\d+$/
171
- rational(aLiteral)
172
- elsif aLiteral =~ /^[+-]?\d+$/
173
- integer(aLiteral)
174
- elsif aLiteral =~ /^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?$/
175
- real(aLiteral)
176
- else
177
- string(aLiteral)
164
+ # if aLiteral =~ /^#t(?:rue)?|true$/
165
+ # boolean(aLiteral)
166
+ # elsif aLiteral =~ /^#f(?:alse)?|false$/
167
+ # boolean(aLiteral)
168
+ # elsif aLiteral =~ /^[+-]?\d+\/\d+$/
169
+ # rational(aLiteral)
170
+ # elsif aLiteral =~ /^[+-]?\d+$/
171
+ # integer(aLiteral)
172
+ # elsif aLiteral =~ /^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?$/
173
+ # real(aLiteral)
174
+ # else
175
+ # string(aLiteral)
176
+ # end
177
+
178
+ case aLiteral
179
+ when /^#t(?:rue)?|true$/, /^#f(?:alse)?|false$/
180
+ boolean(aLiteral)
181
+ when /^[+-]?\d+\/\d+$/
182
+ rational(aLiteral)
183
+ when /^[+-]?\d+$/
184
+ integer(aLiteral)
185
+ when /^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?$/
186
+ real(aLiteral)
187
+ else
188
+ string(aLiteral)
178
189
  end
179
190
  end
180
-
181
191
  end # module
182
- end # module
192
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skeem
2
4
  class SkmElementVisitor
3
5
  # Link to the root element to visit
@@ -12,6 +14,7 @@ module Skeem
12
14
  # @param aRoot [SkmElement] the parse tree to visit.
13
15
  def initialize(aRoot)
14
16
  raise StandardError if aRoot.nil?
17
+
15
18
  @root = aRoot
16
19
  @subscribers = []
17
20
  end
@@ -57,7 +60,6 @@ module Skeem
57
60
  broadcast(:after_empty_list, anEmptyList)
58
61
  end
59
62
 
60
-
61
63
  def visit_pair(aPair)
62
64
  broadcast(:before_pair, aPair)
63
65
  traverse_car_cdr(aPair)
@@ -110,8 +112,9 @@ module Skeem
110
112
  def broadcast(msg, *args)
111
113
  subscribers.each do |subscr|
112
114
  next unless subscr.respond_to?(msg) || subscr.respond_to?(:accept_all)
115
+
113
116
  subscr.send(msg, runtime, *args)
114
117
  end
115
118
  end
116
119
  end # class
117
- end # module
120
+ end # module
data/lib/skeem/grammar.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Grammar for Skeem (a subset of Scheme language)
2
4
  require 'rley' # Load the gem
3
5
 
@@ -7,11 +9,11 @@ module Skeem
7
9
  # Official Small Scheme grammar is available at:
8
10
  # https://bitbucket.org/cowan/r7rs/src/draft-10/rnrs/r7rs.pdf
9
11
  # Names of grammar elements are based on the R7RS documentation
10
- builder = Rley::Syntax::GrammarBuilder.new do
12
+ builder = Rley::grammar_builder do
11
13
  # Delimiters, separators...
12
14
  add_terminals('APOSTROPHE', 'COMMA', 'COMMA_AT_SIGN')
13
15
  add_terminals('GRAVE_ACCENT', 'LPAREN', 'RPAREN')
14
- add_terminals('PERIOD', 'ARROW')
16
+ add_terminals('PERIOD', 'UNDERSCORE', 'ARROW', 'ELLIPSIS')
15
17
  add_terminals('VECTOR_BEGIN')
16
18
 
17
19
  # Literal values...
@@ -19,9 +21,9 @@ module Skeem
19
21
  add_terminals('CHAR', 'STRING_LIT', 'IDENTIFIER')
20
22
 
21
23
  # Keywords...
22
- add_terminals('BEGIN', 'COND', 'DEFINE', 'DO', 'ELSE')
23
- add_terminals('IF', 'LAMBDA', 'LET', 'LET*')
24
- add_terminals('QUOTE', 'QUASIQUOTE', 'SET!')
24
+ add_terminals('BEGIN', 'COND', 'DEFINE', 'DEFINE-SYNTAX', 'DO')
25
+ add_terminals('ELSE', 'IF', 'INCLUDE', 'LAMBDA', 'LET', 'LET_STAR')
26
+ add_terminals('QUOTE', 'QUASIQUOTE', 'SET!', 'SYNTAX-RULES')
25
27
  add_terminals('UNQUOTE', 'UNQUOTE-SPLICING')
26
28
 
27
29
  rule('program' => 'cmd_or_def_plus').as 'main'
@@ -33,7 +35,8 @@ module Skeem
33
35
  rule 'command' => 'expression'
34
36
  rule('definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN').as 'definition'
35
37
  rule('definition' => 'LPAREN DEFINE LPAREN IDENTIFIER def_formals RPAREN body RPAREN').as 'alt_definition'
36
- rule('definition' => 'LPAREN BEGIN definition_star RPAREN').as 'definitions_within_begin'
38
+ rule('definition' => 'syntax_definition')
39
+ rule('definition' => 'LPAREN BEGIN definition* RPAREN').as 'definitions_within_begin'
37
40
  rule('expression' => 'IDENTIFIER').as 'variable_reference'
38
41
  rule 'expression' => 'literal'
39
42
  rule 'expression' => 'procedure_call'
@@ -41,8 +44,9 @@ module Skeem
41
44
  rule 'expression' => 'conditional'
42
45
  rule 'expression' => 'assignment'
43
46
  rule 'expression' => 'derived_expression'
47
+ rule 'expression' => 'includer'
44
48
  rule 'literal' => 'quotation'
45
- rule 'literal' => 'self-evaluating'
49
+ rule 'literal' => 'self-evaluating'
46
50
  rule('quotation' => 'APOSTROPHE datum').as 'quotation_short'
47
51
  rule('quotation' => 'LPAREN QUOTE datum RPAREN').as 'quotation'
48
52
  rule 'self-evaluating' => 'BOOLEAN'
@@ -59,99 +63,118 @@ module Skeem
59
63
  rule 'simple_datum' => 'symbol'
60
64
  rule 'compound_datum' => 'list'
61
65
  rule 'compound_datum' => 'vector'
62
- rule('list' => 'LPAREN datum_star RPAREN').as 'list'
66
+ rule('list' => 'LPAREN datum* RPAREN').as 'list'
63
67
  rule('list' => 'LPAREN datum_plus PERIOD datum RPAREN').as 'dotted_list'
64
- rule('vector' => 'VECTOR_BEGIN datum_star RPAREN').as 'vector'
68
+ rule('vector' => 'VECTOR_BEGIN datum* RPAREN').as 'vector'
65
69
  rule('datum_plus' => 'datum_plus datum').as 'multiple_datums'
66
70
  rule('datum_plus' => 'datum').as 'last_datum'
67
- rule('datum_star' => 'datum_star datum').as 'datum_star'
68
- rule('datum_star' => []).as 'no_datum_yet'
69
71
  rule 'symbol' => 'IDENTIFIER'
70
72
  rule('procedure_call' => 'LPAREN operator RPAREN').as 'proc_call_nullary'
71
- rule('procedure_call' => 'LPAREN operator operand_plus RPAREN').as 'proc_call_args'
72
- rule('operand_plus' => 'operand_plus operand').as 'multiple_operands'
73
- rule('operand_plus' => 'operand').as 'last_operand'
73
+ rule('procedure_call' => 'LPAREN operator operand+ RPAREN').as 'proc_call_args'
74
74
  rule 'operator' => 'expression'
75
75
  rule 'operand' => 'expression'
76
- rule('def_formals' => 'identifier_star').as 'def_formals'
77
- rule('def_formals' => 'identifier_star PERIOD IDENTIFIER').as 'pair_formals'
76
+ rule('def_formals' => 'IDENTIFIER*').as 'def_formals'
77
+ rule('def_formals' => 'IDENTIFIER* PERIOD IDENTIFIER').as 'pair_formals'
78
78
  rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
79
- rule('formals' => 'LPAREN identifier_star RPAREN').as 'fixed_arity_formals'
79
+ rule('formals' => 'LPAREN IDENTIFIER* RPAREN').as 'fixed_arity_formals'
80
80
  rule('formals' => 'IDENTIFIER').as 'variadic_formals'
81
- rule('formals' => 'LPAREN identifier_plus PERIOD IDENTIFIER RPAREN').as 'dotted_formals'
82
- rule('identifier_star' => 'identifier_star IDENTIFIER').as 'identifier_star'
83
- rule('identifier_star' => []).as 'no_identifier_yet'
84
- rule('identifier_plus' => 'identifier_plus IDENTIFIER').as 'multiple_identifiers'
85
- rule('identifier_plus' => 'IDENTIFIER').as 'last_identifier'
86
- rule('body' => 'definition_star sequence').as 'body'
87
- rule('definition_star' => 'definition_star definition').as 'definition_star'
88
- rule('definition_star' => []).as 'no_definition_yet'
89
- rule('sequence' => 'command_star expression').as 'sequence'
90
- rule('command_star' => 'command_star command').as 'multiple_commands'
91
- rule('command_star' => []).as 'no_command_yet'
92
- rule('conditional' => 'LPAREN IF test consequent alternate RPAREN').as 'conditional'
81
+ rule('formals' => 'LPAREN IDENTIFIER+ PERIOD IDENTIFIER RPAREN').as 'dotted_formals'
82
+ rule('syntax_definition' => 'LPAREN DEFINE-SYNTAX keyword transformer_spec RPAREN').as 'syntax_definition'
83
+ rule('body' => 'definition* sequence').as 'body'
84
+ rule('sequence' => 'command* expression').as 'sequence'
85
+ rule('conditional' => 'LPAREN IF test consequent expression? RPAREN').as 'conditional'
93
86
  rule 'test' => 'expression'
94
87
  rule 'consequent' => 'expression'
95
- rule 'alternate' => 'expression'
96
- rule 'alternate' => []
97
88
  rule 'number' => 'INTEGER'
98
89
  rule 'number' => 'RATIONAL'
99
90
  rule 'number' => 'REAL'
100
91
  rule('assignment' => 'LPAREN SET! IDENTIFIER expression RPAREN').as 'assignment'
101
- rule('derived_expression' => 'LPAREN COND cond_clause_plus RPAREN').as 'cond_form'
102
- rule('derived_expression' => 'LPAREN COND cond_clause_star LPAREN ELSE sequence RPAREN RPAREN').as 'cond_else_form'
103
- rule('derived_expression' => 'LPAREN LET LPAREN binding_spec_star RPAREN body RPAREN').as 'short_let_form'
104
- rule('derived_expression' => 'LPAREN LET* LPAREN binding_spec_star RPAREN body RPAREN').as 'let_star_form'
92
+ rule('derived_expression' => 'LPAREN COND cond_clause+ RPAREN').as 'cond_form'
93
+ rule('derived_expression' => 'LPAREN COND cond_clause* LPAREN ELSE sequence RPAREN RPAREN').as 'cond_else_form'
94
+ rule('derived_expression' => 'LPAREN LET LPAREN binding_spec* RPAREN body RPAREN').as 'short_let_form'
95
+ # TODO: implement "named let"
96
+ rule('derived_expression' => 'LPAREN LET IDENTIFIER LPAREN binding_spec* RPAREN body RPAREN') # .as 'named_form'
97
+ rule('derived_expression' => 'LPAREN LET_STAR LPAREN binding_spec* RPAREN body RPAREN').as 'let_star_form'
105
98
 
106
- # As the R7RS grammar is too restrictive,
99
+ # As the R7RS grammar is too restrictive,
107
100
  # the next rule was made more general than its standard counterpart
108
101
  rule('derived_expression' => 'LPAREN BEGIN body RPAREN').as 'begin_expression'
109
102
  do_syntax = <<-END_SYNTAX
110
- LPAREN DO LPAREN iteration_spec_star RPAREN
103
+ LPAREN DO LPAREN iteration_spec* RPAREN
111
104
  LPAREN test do_result RPAREN
112
105
  command_star RPAREN
113
106
  END_SYNTAX
114
107
  rule('derived_expression' => do_syntax).as 'do_expression'
115
108
  rule 'derived_expression' => 'quasiquotation'
116
- rule('cond_clause_plus' => 'cond_clause_plus cond_clause').as 'multiple_cond_clauses'
117
- rule('cond_clause_plus' => 'cond_clause').as 'last_cond_clauses'
118
- rule('cond_clause_star' => 'cond_clause_star cond_clause').as 'cond_clauses_star'
119
- rule('cond_clause_star' => []).as 'last_cond_clauses_star'
120
109
  rule('cond_clause' => 'LPAREN test sequence RPAREN').as 'cond_clause'
121
110
  rule('cond_clause' => 'LPAREN test RPAREN')
122
111
  rule('cond_clause' => 'LPAREN test ARROW recipient RPAREN').as 'cond_arrow_clause'
123
112
  rule('recipient' => 'expression')
124
113
  rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
125
114
  rule('quasiquotation' => 'GRAVE_ACCENT qq_template').as 'quasiquotation_short'
126
- rule('binding_spec_star' => 'binding_spec_star binding_spec').as 'multiple_binding_specs'
127
- rule('binding_spec_star' => []).as 'no_binding_spec_yet'
128
115
  rule('binding_spec' => 'LPAREN IDENTIFIER expression RPAREN').as 'binding_spec'
129
- rule('iteration_spec_star' => 'iteration_spec_star iteration_spec').as 'multiple_iter_specs'
130
- rule('iteration_spec_star' => []).as 'no_iter_spec_yet'
131
116
  rule('iteration_spec' => 'LPAREN IDENTIFIER init step RPAREN').as 'iteration_spec_long'
132
117
  rule('iteration_spec' => 'LPAREN IDENTIFIER init RPAREN').as 'iteration_spec_short'
133
118
  rule('init' => 'expression')
134
119
  rule('step' => 'expression')
135
- rule 'do_result' => 'sequence'
136
- rule('do_result' => []).as 'empty_do_result'
120
+ rule 'do_result' => 'sequence?'
121
+ rule('keyword' => 'IDENTIFIER')
122
+ rule('includer' => 'LPAREN INCLUDE STRING_LIT+ RPAREN').as 'include'
137
123
  rule 'qq_template' => 'simple_datum'
138
124
  rule 'qq_template' => 'list_qq_template'
139
125
  rule 'qq_template' => 'vector_qq_template'
140
126
  rule 'qq_template' => 'unquotation'
141
- rule('list_qq_template' => 'LPAREN qq_template_or_splice_star RPAREN').as 'list_qq'
142
- rule 'list_qq_template' => 'LPAREN qq_template_or_splice_plus PERIOD qq_template RPAREN'
127
+ rule('list_qq_template' => 'LPAREN qq_template_or_splice* RPAREN').as 'list_qq'
128
+ rule 'list_qq_template' => 'LPAREN qq_template_or_splice+ PERIOD qq_template RPAREN'
143
129
  rule 'list_qq_template' => 'GRAVE_ACCENT qq_template'
144
- rule('vector_qq_template' => 'VECTOR_BEGIN qq_template_or_splice_star RPAREN').as 'vector_qq'
130
+ rule('vector_qq_template' => 'VECTOR_BEGIN qq_template_or_splice* RPAREN').as 'vector_qq'
145
131
  rule('unquotation' => 'COMMA qq_template').as 'unquotation_short'
146
132
  rule 'unquotation' => 'LPAREN UNQUOTE qq_template RPAREN'
147
- rule('qq_template_or_splice_star' => 'qq_template_or_splice_star qq_template_or_splice').as 'multiple_template_splice'
148
- rule('qq_template_or_splice_star' => []).as 'no_template_splice_yet'
149
- rule 'qq_template_or_splice_plus' => 'qq_template_or_splice_plus qq_template_or_splice'
150
- rule 'qq_template_or_splice_plus' => 'qq_template_or_splice'
151
133
  rule 'qq_template_or_splice' => 'qq_template'
152
134
  rule 'qq_template_or_splice' => 'splicing_unquotation'
153
135
  rule 'splicing_unquotation' => 'COMMA_AT_SIGN qq_template'
154
136
  rule 'splicing_unquotation' => 'LPAREN UNQUOTE-SPLICING qq_template RPAREN'
137
+ rule('transformer_spec' => 'LPAREN SYNTAX-RULES LPAREN IDENTIFIER* RPAREN syntax_rule* RPAREN').as 'transformer_syntax'
138
+ rule('transformer_spec' => 'LPAREN SYNTAX-RULES IDENTIFIER LPAREN IDENTIFIER* RPAREN syntax_rule* RPAREN')
139
+ rule('syntax_rule' => 'LPAREN pattern template RPAREN').as 'syntax_rule'
140
+ rule('pattern' => 'pattern_identifier')
141
+ rule('pattern' => 'UNDERSCORE')
142
+ rule('pattern' => 'LPAREN pattern* RPAREN')
143
+ rule('pattern' => 'LPAREN pattern+ PERIOD pattern RPAREN')
144
+ rule('pattern' => 'LPAREN pattern+ ELLIPSIS pattern* RPAREN')
145
+ rule('pattern' => 'LPAREN pattern+ ELLIPSIS pattern* PERIOD pattern RPAREN')
146
+ rule('pattern' => 'VECTOR_BEGIN pattern* RPAREN')
147
+ rule('pattern' => 'VECTOR_BEGIN pattern+ ELLIPSIS pattern* RPAREN')
148
+ rule('pattern' => 'pattern_datum')
149
+ rule('pattern_datum' => 'STRING_LIT')
150
+ rule('pattern_datum' => 'CHAR')
151
+ rule('pattern_datum' => 'BOOLEAN')
152
+ rule('pattern_datum' => 'number')
153
+ # rule('pattern_datum' => 'bytevector')
154
+ rule('template' => 'pattern_identifier')
155
+ rule('template' => 'LPAREN template_element* RPAREN')
156
+ rule('template' => 'LPAREN template_element+ PERIOD template RPAREN')
157
+ rule('template' => 'VECTOR_BEGIN template_element* RPAREN')
158
+ rule('template' => 'template_datum')
159
+ rule('template_element' => 'template')
160
+ rule('template_element' => 'template ELLIPSIS')
161
+ rule('template_datum' => 'pattern_datum')
162
+ rule('pattern_identifier' => 'IDENTIFIER')
163
+ # Ugly: specialized production rule per keyword...
164
+ rule('pattern_identifier' => 'BEGIN')
165
+ rule('pattern_identifier' => 'COND')
166
+ rule('pattern_identifier' => 'DEFINE')
167
+ rule('pattern_identifier' => 'ELSE')
168
+ rule('pattern_identifier' => 'IF')
169
+ rule('pattern_identifier' => 'INCLUDE')
170
+ rule('pattern_identifier' => 'LAMBDA')
171
+ rule('pattern_identifier' => 'LET')
172
+ rule('pattern_identifier' => 'LET*')
173
+ rule('pattern_identifier' => 'QUOTE')
174
+ rule('pattern_identifier' => 'QUASIQUOTE')
175
+ rule('pattern_identifier' => 'SET!')
176
+ rule('pattern_identifier' => 'UNQUOTE')
177
+ rule('pattern_identifier' => 'UNQUOTE-SPLICING')
155
178
  end
156
179
 
157
180
  # And now build the grammar and make it accessible via a global constant