skeem 0.2.15 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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