skeem 0.0.23 → 0.0.24

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 46a43c0a96d793c7f042976ec034e9e261fe8198
4
- data.tar.gz: 381a4e60b09bc4ffe797dce9ed62a40b53ec1225
3
+ metadata.gz: e1f6bf0ae4407a85eb404c02c2497964744c26b2
4
+ data.tar.gz: f66450bf98cb129a7e40499bf589ef6454760c12
5
5
  SHA512:
6
- metadata.gz: e7dc6aa941a1e5fc4940b4d35df274ac08cf3064990dd033af703d3e72afc59f7abfd19249c24574162fdac5e72fb74425e54303780df215764fb800d19b4cb6
7
- data.tar.gz: a716f8595373f25da0796eb58c07f53d47bb83db183fe0d969226ff3216993198f5c9a47cb6bea7e3e578e539eb1760c9effbf8426483b59fcd34058be5c362a
6
+ metadata.gz: f07d638db941e45c54717ba99addfdb627ba9c76ace292905bb3fa3687888a42a633f51cc9dc6975d058f6995782e56f9a7439cc8e0a0086c1a4ba93be84040a
7
+ data.tar.gz: 03b924927a3cf1e08780de16743403ab17ca68646e17f1f1b36bcfc58f3478cf403d09c2cf0626306f83dc60e6b06c4a9df57502500ca246361bbcc641833bc7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## [0.0.24] - 2018-11-08
2
+ Many internal refactoring, augmented spec files base, initial quasiquotation implementation.
3
+
4
+ ### Added
5
+ - File `primitive_builder.rb` implementation of standard: `string->length`
6
+ - File `datum_dsl.rb` to implement an internal DSL for building literal data elements.
7
+ - Files `tokenizer.rb`, `grammar.rb`, `class SExprBuilder` Added support for quasiquotation: (quasiquote foo) or `foo and unquoting
8
+
9
+ ### Changed
10
+ - File `README.md` udpated to reflect currently implemented features.
11
+
1
12
  ## [0.0.23] - 2018-10-24
2
13
  ### Added
3
14
  - File `primitive_builder.rb` implementation of: standard `or`, `string->symbol`, `number->string`, `string-append` procedures.
data/README.md CHANGED
@@ -40,11 +40,6 @@ Here are a few pointers for the Scheme programming language:
40
40
  - [The Scheme Programming Language, 4th Edition](https://www.scheme.com/tspl4/) by Kent Dybvig. An complete, introductory textbook on Scheme based on the older R5RS standard.
41
41
  - [Teach Yourself Scheme in Fixnum Days](http://ds26gte.github.io/tyscheme/index.html) by Dorai Sitaram
42
42
 
43
- ## Other similar Ruby projects
44
- __Skeem__ isn't the sole implementation of the Scheme language in Ruby.
45
- Here are a few other ones:
46
- - [Heist gem](https://rubygems.org/gems/heist) -- Probably one of best Scheme implementation in Ruby. Really worth a try. Alas, the [project](https://github.com/jcoglan/heist) seems to be dormant for several years.
47
-
48
43
  ## Usage
49
44
 
50
45
  ### Example 1 (Variable definition)
@@ -120,6 +115,7 @@ Here are a few other ones:
120
115
  - Of the number hierarchy:
121
116
  `real` (e.g. 2.718, 6.671e-11),
122
117
  `integer` (42, -3)
118
+ - Lists (quoted) : '(1 two "three")
123
119
  - Strings: `"Hello, world."`
124
120
  - Identifiers (symbols): `really-cool-procedure`
125
121
  - Vectors: `#(1 2 "three")`
@@ -127,6 +123,7 @@ Here are a few other ones:
127
123
  ### Scheme Expressions
128
124
  - Constant literals
129
125
  - Quotations
126
+ - Quasiquotation (without unquote-splicing)
130
127
  - Variable references
131
128
  - Procedure calls
132
129
  - Lambda expressions
@@ -173,7 +170,7 @@ This section lists the implemented standard procedures
173
170
  * `list?`, `null?`, `list`, `length`
174
171
 
175
172
  #### String procedures
176
- * `string?`, `string-append`, `string->symbol`,
173
+ * `string?`, `string-append`, `string-length`, `string->symbol`,
177
174
 
178
175
  #### Symbol procedures
179
176
  * `symbol?`
@@ -193,6 +190,14 @@ Roadmap:
193
190
  - Extend the language in order to support [Minikanren](https://github.com/TheReasonedSchemer2ndEd/CodeFromTheReasonedSchemer2ndEd)
194
191
  - Make it pass all examples from the [Reasoned Schemer](https://mitpress.mit.edu/books/reasoned-schemer-second-edition) book.
195
192
 
193
+ ## Other Scheme implementation in Ruby
194
+ __Skeem__ isn't the sole implementation of the Scheme language in Ruby.
195
+ Here are a few other ones:
196
+ - [Heist gem](https://rubygems.org/gems/heist) -- Probably one of best Scheme implementation in Ruby. Really worth a try. Alas, the [project](https://github.com/jcoglan/heist) seems to be dormant for several years.
197
+ - [Schemerald gem](https://rubygems.org/gems/schemerald). The last commit for the [project](https://github.com/vntzy/schemerald) is October 2017.
198
+
199
+ - [rubic gem](https://rubygems.org/gems/rubic). The last commit for the [project](https://github.com/notozeki/rubic) is June 2015.
200
+
196
201
  ## Contributing
197
202
 
198
203
  Bug reports and pull requests are welcome on GitHub at https://github.com/famished-tiger/Skeem.
@@ -0,0 +1,139 @@
1
+ require_relative 'skm_simple_datum'
2
+ require_relative 'skm_compound_datum'
3
+
4
+ module Skeem
5
+ # Mixin module that provides factory methods that ease the conversion of
6
+ # Ruby literals into SkmSimpleDatum or SkmCompoundDatum objects.
7
+ module DatumDSL
8
+ def boolean(aBoolean)
9
+ return aBoolean if aBoolean.kind_of?(SkmBoolean)
10
+
11
+ result = case aBoolean
12
+ when TrueClass, FalseClass
13
+ SkmBoolean.create(aBoolean)
14
+ when /^#t(?:rue)?|true$/
15
+ SkmBoolean.create(true)
16
+ when /^#f(?:alse)?|false$/
17
+ SkmBoolean.create(false)
18
+ else
19
+ raise StandardError, aBoolean.inspect
20
+ end
21
+ end
22
+
23
+ def integer(aLiteral)
24
+ return aLiteral if aLiteral.kind_of?(SkmInteger)
25
+
26
+ result = case aLiteral
27
+ when Integer
28
+ SkmInteger.create(aLiteral)
29
+ when /^[+-]?\d+$/
30
+ SkmInteger.create(aLiteral.to_i)
31
+ else
32
+ raise StandardError, aLiteral.inspect
33
+ end
34
+ end
35
+
36
+ def real(aLiteral)
37
+ return aLiteral if aLiteral.kind_of?(SkmReal)
38
+
39
+ result = case aLiteral
40
+ when Numeric
41
+ SkmReal.create(aLiteral)
42
+ when /^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?$/
43
+ SkmReal.create(aLiteral.to_f)
44
+ else
45
+ raise StandardError, aLiteral.inspect
46
+ end
47
+ end
48
+
49
+ def string(aLiteral)
50
+ return aLiteral if aLiteral.kind_of?(SkmString)
51
+
52
+ result = case aLiteral
53
+ when String
54
+ SkmString.create(aLiteral)
55
+ else
56
+ SkmString.create(aLiteral.to_s)
57
+ end
58
+ end
59
+
60
+ def identifier(aLiteral)
61
+ return aLiteral if aLiteral.kind_of?(SkmIdentifier)
62
+
63
+ result = case aLiteral
64
+ when String
65
+ SkmIdentifier.create(aLiteral)
66
+ when SkmString
67
+ SkmIdentifier.create(aLiteral.value)
68
+ else
69
+ raise StandardError, aLiteral.inspect
70
+ end
71
+ end
72
+
73
+ alias symbol identifier
74
+
75
+ def list(aLiteral)
76
+ result = case aLiteral
77
+ when Array
78
+ SkmList.new(to_datum(aLiteral))
79
+ when SkmList
80
+ SkmList.new(to_datum(aLiteral.members))
81
+ else
82
+ SkmList.new([to_datum(aLiteral)])
83
+ end
84
+
85
+ result
86
+ end
87
+
88
+ def vector(aLiteral)
89
+ result = case aLiteral
90
+ when Array
91
+ SkmVector.new(to_datum(aLiteral))
92
+ when SkmVector
93
+ SkmVector.new(to_datum(aLiteral.members))
94
+ else
95
+ SkmVector.new([to_datum(aLiteral)])
96
+ end
97
+
98
+ result
99
+ end
100
+
101
+ def to_datum(aLiteral)
102
+ return aLiteral if aLiteral.kind_of?(SkmSimpleDatum)
103
+ return list(aLiteral.members) if aLiteral.kind_of?(SkmList)
104
+ return vector(aLiteral.members) if aLiteral.kind_of?(SkmVector)
105
+
106
+ result = case aLiteral
107
+ when Array
108
+ aLiteral.map { |elem| to_datum(elem) }
109
+ when Integer
110
+ SkmInteger.create(aLiteral)
111
+ when Float
112
+ SkmReal.create(aLiteral)
113
+ when TrueClass, FalseClass
114
+ SkmBoolean.create(aLiteral)
115
+ when String
116
+ parse_literal(aLiteral)
117
+ else
118
+ raise StandardError, aLiteral.inspect
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def parse_literal(aLiteral)
125
+ if aLiteral =~ /^#t(?:rue)?|true$/
126
+ boolean(aLiteral)
127
+ elsif aLiteral =~ /^#f(?:alse)?|false$/
128
+ boolean(aLiteral)
129
+ elsif aLiteral =~ /^[+-]?\d+$/
130
+ integer(aLiteral)
131
+ elsif aLiteral =~ /^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?$/
132
+ real(aLiteral)
133
+ else
134
+ string(aLiteral)
135
+ end
136
+ end
137
+
138
+ end # module
139
+ end # module
@@ -0,0 +1,91 @@
1
+ module Skeem
2
+ class SkmElementVisitor
3
+ # Link to the root element to visit
4
+ attr_reader(:root)
5
+
6
+ # List of objects that subscribed to the visit event notification.
7
+ attr_reader(:subscribers)
8
+
9
+ attr_reader(:runtime)
10
+
11
+ # Build a visitor for the given root.
12
+ # @param aRoot [SkmElement] the parse tree to visit.
13
+ def initialize(aRoot)
14
+ raise StandardError if aRoot.nil?
15
+ @root = aRoot
16
+ @subscribers = []
17
+ end
18
+
19
+ # Add a subscriber for the visit event notifications.
20
+ # @param aSubscriber [Object]
21
+ def subscribe(aSubscriber)
22
+ subscribers << aSubscriber
23
+ end
24
+
25
+ # Remove the given object from the subscription list.
26
+ # The object won't be notified of visit events.
27
+ # @param aSubscriber [Object]
28
+ def unsubscribe(aSubscriber)
29
+ subscribers.delete_if { |entry| entry == aSubscriber }
30
+ end
31
+
32
+ # The signal to begin the visit of the root.
33
+ def start(aRuntime)
34
+ @runtime = aRuntime
35
+ root.accept(self)
36
+ end
37
+
38
+ # Visit event. The visitor is visiting the
39
+ # given simple datum object.
40
+ # @param aTerminal [SkmTerminal] the terminal to visit.
41
+ def visit_simple_datum(aSimpleDatum)
42
+ broadcast(:before_simple_datum, aSimpleDatum)
43
+ broadcast(:after_simple_datum, aSimpleDatum)
44
+ end
45
+
46
+ def visit_compound_datum(aCompoundDatum)
47
+ broadcast(:before_compound_datum, aCompoundDatum)
48
+ traverse_children(aCompoundDatum)
49
+ broadcast(:after_compound_datum, aCompoundDatum)
50
+ end
51
+
52
+
53
+
54
+ =begin
55
+ # Visit event. The visitor is about to visit the given non terminal node.
56
+ # @param aNonTerminalNode [NonTerminalNode] the node to visit.
57
+ def visit_nonterminal(aNonTerminalNode)
58
+ if @traversal == :post_order
59
+ broadcast(:before_non_terminal, aNonTerminalNode)
60
+ traverse_subnodes(aNonTerminalNode)
61
+ else
62
+ traverse_subnodes(aNonTerminalNode)
63
+ broadcast(:before_non_terminal, aNonTerminalNode)
64
+ end
65
+ broadcast(:after_non_terminal, aNonTerminalNode)
66
+ end
67
+ =end
68
+
69
+ private
70
+
71
+ def traverse_children(aParent)
72
+ children = aParent.children
73
+ broadcast(:before_children, aParent, children)
74
+
75
+ # Let's proceed with the visit of children
76
+ children.each { |a_child| a_child.accept(self) }
77
+
78
+ broadcast(:after_children, aParent, children)
79
+ end
80
+
81
+ # Send a notification to all subscribers.
82
+ # @param msg [Symbol] event to notify
83
+ # @param args [Array] arguments of the notification.
84
+ def broadcast(msg, *args)
85
+ subscribers.each do |subscr|
86
+ next unless subscr.respond_to?(msg) || subscr.respond_to?(:accept_all)
87
+ subscr.send(msg, runtime, *args)
88
+ end
89
+ end
90
+ end # class
91
+ end # module
data/lib/skeem/grammar.rb CHANGED
@@ -9,8 +9,8 @@ module Skeem
9
9
  # Names of grammar elements are based on the R7RS documentation
10
10
  builder = Rley::Syntax::GrammarBuilder.new do
11
11
  # Delimiters, separators...
12
- add_terminals('APOSTROPHE') #, 'BACKQUOTE')
13
- add_terminals('LPAREN', 'RPAREN')
12
+ add_terminals('APOSTROPHE', 'COMMA', 'COMMA_AT_SIGN')
13
+ add_terminals('GRAVE_ACCENT', 'LPAREN', 'RPAREN')
14
14
  add_terminals('PERIOD')
15
15
  add_terminals('VECTOR_BEGIN')
16
16
 
@@ -20,7 +20,8 @@ module Skeem
20
20
 
21
21
  # Keywords...
22
22
  add_terminals('DEFINE', 'IF', 'LAMBDA')
23
- add_terminals('QUOTE')
23
+ add_terminals('QUOTE', 'QUASIQUOTE', 'UNQUOTE')
24
+ add_terminals('UNQUOTE-SPLICING')
24
25
 
25
26
  rule('program' => 'cmd_or_def_plus').as 'main'
26
27
  rule('cmd_or_def_plus' => 'cmd_or_def_plus cmd_or_def').as 'multiple_cmd_def'
@@ -29,15 +30,16 @@ module Skeem
29
30
  rule 'cmd_or_def' => 'definition'
30
31
  rule 'command' => 'expression'
31
32
  rule('definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN').as 'definition'
32
- rule('definition' => 'LPAREN DEFINE LPAREN IDENTIFIER def_formals RPAREN body RPAREN').as 'alt_definition'
33
+ rule('definition' => 'LPAREN DEFINE LPAREN IDENTIFIER def_formals RPAREN body RPAREN').as 'alt_definition'
33
34
  rule('expression' => 'IDENTIFIER').as 'variable_reference'
34
35
  rule 'expression' => 'literal'
35
36
  rule 'expression' => 'procedure_call'
36
37
  rule 'expression' => 'lambda_expression'
37
38
  rule 'expression' => 'conditional'
39
+ rule 'expression' => 'derived_expression'
38
40
  rule 'literal' => 'quotation'
39
41
  rule 'literal' => 'self-evaluating'
40
- rule('quotation' => 'APOSTROPHE datum').as 'quotation_abbrev'
42
+ rule('quotation' => 'APOSTROPHE datum').as 'quotation_short'
41
43
  rule('quotation' => 'LPAREN QUOTE datum RPAREN').as 'quotation'
42
44
  rule 'self-evaluating' => 'BOOLEAN'
43
45
  rule 'self-evaluating' => 'number'
@@ -47,7 +49,7 @@ module Skeem
47
49
  rule 'datum' => 'compound_datum'
48
50
  rule 'simple_datum' => 'BOOLEAN'
49
51
  rule 'simple_datum' => 'number'
50
- rule 'simple_datum' => 'STRING_LIT'
52
+ rule 'simple_datum' => 'STRING_LIT'
51
53
  rule 'simple_datum' => 'symbol'
52
54
  rule 'compound_datum' => 'list'
53
55
  rule 'compound_datum' => 'vector'
@@ -55,7 +57,7 @@ module Skeem
55
57
  rule 'list' => 'LPAREN datum_plus PERIOD datum RPAREN'
56
58
  rule('vector' => 'VECTOR_BEGIN datum_star RPAREN').as 'vector'
57
59
  rule('datum_plus' => 'datum_plus datum').as 'multiple_datums'
58
- rule('datum_plus' => 'datum').as 'last_datum'
60
+ rule('datum_plus' => 'datum').as 'last_datum'
59
61
  rule('datum_star' => 'datum_star datum').as 'datum_star'
60
62
  rule('datum_star' => []).as 'no_datum_yet'
61
63
  rule 'symbol' => 'IDENTIFIER'
@@ -66,7 +68,7 @@ module Skeem
66
68
  rule 'operator' => 'expression'
67
69
  rule 'operand' => 'expression'
68
70
  rule('def_formals' => 'identifier_star').as 'def_formals'
69
- rule('def_formals' => 'identifier_star PERIOD IDENTIFIER').as 'pair_formals'
71
+ rule('def_formals' => 'identifier_star PERIOD IDENTIFIER').as 'pair_formals'
70
72
  rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
71
73
  rule('formals' => 'LPAREN identifier_star RPAREN').as 'fixed_arity_formals'
72
74
  rule('formals' => 'IDENTIFIER').as 'variadic_formals'
@@ -88,6 +90,27 @@ module Skeem
88
90
  rule 'alternate' => []
89
91
  rule 'number' => 'INTEGER'
90
92
  rule 'number' => 'REAL'
93
+ rule 'derived_expression' => 'quasiquotation'
94
+ rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
95
+ rule('quasiquotation' => 'GRAVE_ACCENT qq_template').as 'quasiquotation_short'
96
+ rule 'qq_template' => 'simple_datum'
97
+ rule 'qq_template' => 'list_qq_template'
98
+ rule 'qq_template' => 'vector_qq_template'
99
+ rule 'qq_template' => 'unquotation'
100
+ rule('list_qq_template' => 'LPAREN qq_template_or_splice_star RPAREN').as 'list_qq'
101
+ rule 'list_qq_template' => 'LPAREN qq_template_or_splice_plus PERIOD qq_template RPAREN'
102
+ rule 'list_qq_template' => 'GRAVE_ACCENT qq_template'
103
+ rule('vector_qq_template' => 'VECTOR_BEGIN qq_template_or_splice_star RPAREN').as 'vector_qq'
104
+ rule('unquotation' => 'COMMA qq_template').as 'unquotation_short'
105
+ rule 'unquotation' => 'LPAREN UNQUOTE qq_template RPAREN'
106
+ rule('qq_template_or_splice_star' => 'qq_template_or_splice_star qq_template_or_splice').as 'multiple_template_splice'
107
+ rule('qq_template_or_splice_star' => []).as 'no_template_splice_yet'
108
+ rule 'qq_template_or_splice_plus' => 'qq_template_or_splice_plus qq_template_or_splice'
109
+ rule 'qq_template_or_splice_plus' => 'qq_template_or_splice'
110
+ rule 'qq_template_or_splice' => 'qq_template'
111
+ rule 'qq_template_or_splice' => 'splicing_unquotation'
112
+ rule 'splicing_unquotation' => 'COMMA_AT_SIGN qq_template'
113
+ rule 'splicing_unquotation' => 'LPAREN UNQUOTE-SPLICING qq_template RPAREN'
91
114
  end
92
115
 
93
116
  # And now build the grammar and make it accessible via a global constant
@@ -22,7 +22,7 @@ module Skeem
22
22
  # require 'debug' if mode.nil?
23
23
  return @ptree.root.evaluate(runtime)
24
24
  end
25
-
25
+
26
26
  def fetch(anIdentifier)
27
27
  runtime.environment.fetch(anIdentifier)
28
28
  end