skeem 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +11 -3
- data/lib/skeem.rb +7 -4
- data/lib/skeem/grammar.rb +42 -0
- data/lib/skeem/parser.rb +36 -0
- data/lib/skeem/tokenizer.rb +9 -1
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/parser_spec.rb +26 -0
- data/spec/skeem/tokenizer_spec.rb +50 -4
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 670bb82d2fecac647938c03d92b8c910f9385a49
|
4
|
+
data.tar.gz: 3ac772b051b36ded30aefe62c6745239c9a1941f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b8488389ba2a11fa1664bb6f3e2a40c752a16045061f516360cce2744e9e05e2746d823f8854fa40f23fe24183105384863f19156a7b425a86d95071d2ad9c4
|
7
|
+
data.tar.gz: 1c6029799408c83027072cdfca72f9d86dd6bbf5519af149fbb3736e4224f223f553c1463e56fd0461b0e977ae91a2be14ab2a8f4355e9a1ae5449094167a08b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## [0.0.3] - 2018-08-25
|
2
|
+
### Added
|
3
|
+
- File `grammar.rb` with minimalist grammar.
|
4
|
+
- Initial `Parser` class commit
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
- Class`Tokenizer` recognizes `define` keyword
|
8
|
+
- Spec file `Tokenizer_spec.rb` expanded with more tests.
|
9
|
+
|
1
10
|
## [0.0.2] - 2018-08-25
|
2
11
|
### Changed
|
3
12
|
- Class`Tokenizer` improved, does recognize delimiters, booleans, integers, real numbers, strings, and identifiers.
|
data/README.md
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
# Skeem
|
2
|
+
[](https://travis-ci.org/famished-tiger/Skeem)
|
3
|
+
[](https://badge.fury.io/rb/skeem)
|
4
|
+
[](https://github.com/famished-tiger/Skeem/blob/master/LICENSE.txt)
|
5
|
+
|
2
6
|
__Skeem__ will be an interpreter of a subset of the Scheme programming language.
|
3
7
|
|
4
8
|
|
@@ -20,8 +24,12 @@ Or install it yourself as:
|
|
20
24
|
|
21
25
|
## Usage
|
22
26
|
|
23
|
-
The __Skeem__ project has just started.
|
24
|
-
At this stage,
|
27
|
+
The __Skeem__ project has just started.
|
28
|
+
At this stage, the gem consists of a tokenizer.
|
29
|
+
At least these steps must be done in order to have a first interpreter:
|
30
|
+
- TODO: define subset of Scheme grammar
|
31
|
+
- TODO: buuild parser of Scheme subset
|
32
|
+
- TODO: Implement Skeem interpreter (and REPL)
|
25
33
|
|
26
34
|
Roadmap:
|
27
35
|
- Implement an equivalent of [lis.py](http://www.norvig.com/lispy.html)
|
@@ -33,7 +41,7 @@ Roadmap:
|
|
33
41
|
TODO: Write usage instructions here
|
34
42
|
|
35
43
|
Good to know:
|
36
|
-
Online book: [The Scheme Programming Language (4th Ed.)](https://www.scheme.com/tspl4/)
|
44
|
+
Online book: [The Scheme Programming Language (4th Ed.)](https://www.scheme.com/tspl4/). Remark: covers an older version of Scheme.
|
37
45
|
|
38
46
|
## Development
|
39
47
|
|
data/lib/skeem.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
1
|
+
# File: skeem.rb
|
2
|
+
# This file acts as a jumping-off point for loading dependencies expected
|
3
|
+
# for a Skeem client.
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
5
|
+
require_relative './skeem/version'
|
6
|
+
require_relative './skeem/parser'
|
7
|
+
|
8
|
+
# End of file
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Grammar for Skeem (a subset of Scheme language)
|
2
|
+
require 'rley' # Load the gem
|
3
|
+
|
4
|
+
module Skeem
|
5
|
+
########################################
|
6
|
+
# Define a grammar for Skeem
|
7
|
+
# Official Small Scheme grammar is available at:
|
8
|
+
# https://bitbucket.org/cowan/r7rs/src/draft-10/rnrs/r7rs.pdf
|
9
|
+
# Names of grammar elements are based on the R7RS documentation
|
10
|
+
builder = Rley::Syntax::GrammarBuilder.new do
|
11
|
+
# Delimitersn, separators...
|
12
|
+
# add_terminals('APOSTROPHE', 'BACKQUOTE')
|
13
|
+
add_terminals('LPAREN', 'RPAREN')
|
14
|
+
# add_terminals('PERIOD')
|
15
|
+
|
16
|
+
# Literal values...
|
17
|
+
add_terminals('BOOLEAN', 'INTEGER', 'REAL')
|
18
|
+
add_terminals('STRING_LIT', 'IDENTIFIER')
|
19
|
+
|
20
|
+
# Keywords...
|
21
|
+
# add_terminals('BEGIN', 'DEFINE')
|
22
|
+
add_terminals('DEFINE')
|
23
|
+
|
24
|
+
rule 'program' => 'cmd_or_def_plus'
|
25
|
+
rule 'cmd_or_def_plus' => 'cmd_or_def_plus cmd_or_def'
|
26
|
+
rule 'cmd_or_def_plus' => 'cmd_or_def'
|
27
|
+
rule 'cmd_or_def' => 'definition'
|
28
|
+
rule 'definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN'
|
29
|
+
rule 'expression' => 'IDENTIFIER'
|
30
|
+
rule 'expression' => 'literal'
|
31
|
+
rule 'literal' => 'self-evaluating'
|
32
|
+
rule 'self-evaluating' => 'BOOLEAN'
|
33
|
+
rule 'self-evaluating' => 'number'
|
34
|
+
rule 'self-evaluating' => 'STRING_LIT'
|
35
|
+
rule 'number' => 'INTEGER'
|
36
|
+
rule 'number' => 'REAL'
|
37
|
+
end
|
38
|
+
|
39
|
+
# And now build the grammar and make it accessible via a global constant
|
40
|
+
# [Rley::Syntax::Grammar]
|
41
|
+
Grammar = builder.grammar
|
42
|
+
end # module
|
data/lib/skeem/parser.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative 'tokenizer'
|
2
|
+
require_relative 'grammar'
|
3
|
+
|
4
|
+
module Skeem
|
5
|
+
class Parser
|
6
|
+
attr_reader(:engine)
|
7
|
+
|
8
|
+
def initialize()
|
9
|
+
# Create a Rley facade object
|
10
|
+
@engine = Rley::Engine.new { |cfg| cfg.diagnose = true }
|
11
|
+
|
12
|
+
# Step 1. Load Skeem grammar
|
13
|
+
@engine.use_grammar(Skeem::Grammar)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Parse the given Skeem expression into a parse tree.
|
17
|
+
# @param source [String] Skeem expression to parse
|
18
|
+
# @return [ParseTree] A regexp object equivalent to the Skeem expression.
|
19
|
+
# @example Defining a function that computes the area of a circle
|
20
|
+
# source = "(define circle-area (lambda (r) (* pi (* r r))))"
|
21
|
+
# regex = Skeem::parse(source)
|
22
|
+
def parse(source)
|
23
|
+
lexer = Skeem::Tokenizer.new(source)
|
24
|
+
result = engine.parse(lexer.tokens)
|
25
|
+
|
26
|
+
unless result.success?
|
27
|
+
# Stop if the parse failed...
|
28
|
+
line1 = "Parsing failed\n"
|
29
|
+
line2 = "Reason: #{result.failure_reason.message}"
|
30
|
+
raise StandardError, line1 + line2
|
31
|
+
end
|
32
|
+
|
33
|
+
return result
|
34
|
+
end
|
35
|
+
end # class
|
36
|
+
end # module
|
data/lib/skeem/tokenizer.rb
CHANGED
@@ -24,6 +24,12 @@ module Skeem
|
|
24
24
|
'(' => 'LPAREN',
|
25
25
|
')' => 'RPAREN'
|
26
26
|
}.freeze
|
27
|
+
|
28
|
+
# Here are all the SRL keywords (in uppercase)
|
29
|
+
@@keywords = %w[
|
30
|
+
BEGIN
|
31
|
+
DEFINE
|
32
|
+
].map { |x| [x, x] } .to_h
|
27
33
|
|
28
34
|
class ScanError < StandardError; end
|
29
35
|
|
@@ -77,7 +83,9 @@ module Skeem
|
|
77
83
|
unquoted = lexeme.gsub(/(^")|("$)/, '')
|
78
84
|
token = build_token('STRING_LIT', unquoted)
|
79
85
|
elsif (lexeme = scanner.scan(/[a-zA-Z!$%&*\/:<=>?@^_~][a-zA-Z0-9!$%&*+-.\/:<=>?@^_~+-]*/))
|
80
|
-
|
86
|
+
keyw = @@keywords[lexeme.upcase]
|
87
|
+
tok_type = keyw ? keyw : 'IDENTIFIER'
|
88
|
+
token = build_token(tok_type, lexeme)
|
81
89
|
elsif (lexeme = scanner.scan(/\|(?:[^|])*\|/)) # Vertical bar delimited
|
82
90
|
token = build_token('IDENTIFIER', lexeme)
|
83
91
|
elsif (lexeme = scanner.scan(/([\+\-])((?=\s|[|()";])|$)/))
|
data/lib/skeem/version.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../spec_helper' # Use the RSpec framework
|
2
|
+
require_relative '../../lib/skeem/tokenizer' # Load the class under test
|
3
|
+
|
4
|
+
module Skeem
|
5
|
+
describe Parser do
|
6
|
+
|
7
|
+
context 'Initialization:' do
|
8
|
+
it 'should be initialized without argument' do
|
9
|
+
expect { Parser.new() }.not_to raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should have its enginer initialized' do
|
13
|
+
expect(subject.engine).to be_kind_of(Rley::Engine)
|
14
|
+
end
|
15
|
+
end # context
|
16
|
+
|
17
|
+
context 'Parsing:' do
|
18
|
+
it 'should parse definitions' do
|
19
|
+
source = "(define r 10)"
|
20
|
+
expect { subject.parse(source) }.not_to raise_error
|
21
|
+
end
|
22
|
+
end # context
|
23
|
+
end # describe
|
24
|
+
end # module
|
25
|
+
|
26
|
+
# End of file
|
@@ -10,7 +10,7 @@ module Skeem
|
|
10
10
|
expect(token.lexeme).to eq(lexeme)
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def unquoted(aString)
|
15
15
|
aString.gsub(/(^")|("$)/, '')
|
16
16
|
end
|
@@ -141,18 +141,64 @@ containing just one line"
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
end # context
|
144
|
-
|
144
|
+
|
145
145
|
context 'Scanning Scheme sample code' do
|
146
146
|
it 'should read examples from lis.py page' do
|
147
|
+
# Shallow tokenizer testing
|
147
148
|
source = <<-SCHEME
|
148
|
-
(if (> (val x) 0)
|
149
|
-
(fn (+ (aref A i) (* 3 i))
|
149
|
+
(if (> (val x) 0)
|
150
|
+
(fn (+ (aref A i) (* 3 i))
|
150
151
|
(quote (one two)))
|
151
152
|
end
|
152
153
|
end
|
153
154
|
SCHEME
|
154
155
|
subject.reinitialize(source)
|
155
156
|
expect { subject.tokens }.not_to raise_error
|
157
|
+
|
158
|
+
source = "(define circle-area (lambda (r) (* pi (* r r))))"
|
159
|
+
subject.reinitialize(source)
|
160
|
+
expect { subject.tokens }.not_to raise_error
|
161
|
+
|
162
|
+
source = "(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))"
|
163
|
+
subject.reinitialize(source)
|
164
|
+
expect { subject.tokens }.not_to raise_error
|
165
|
+
|
166
|
+
source = <<-SCHEME
|
167
|
+
define first car)
|
168
|
+
define rest cdr)
|
169
|
+
define count (lambda (item L) (if L (+ (equal? item (first L)) (count item (rest L))) 0)))
|
170
|
+
count 0 (list 0 1 2 3 0 0))
|
171
|
+
SCHEME
|
172
|
+
subject.reinitialize(source)
|
173
|
+
expect { subject.tokens }.not_to raise_error
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should produce a sequence of token objects' do
|
177
|
+
# Deeper tokenizer testing
|
178
|
+
source = "(define circle-area (lambda (r) (* pi (* r r))))"
|
179
|
+
subject.reinitialize(source)
|
180
|
+
predicted = [
|
181
|
+
['LPAREN', '('],
|
182
|
+
['DEFINE', 'define'],
|
183
|
+
['IDENTIFIER', 'circle-area'],
|
184
|
+
['LPAREN', '('],
|
185
|
+
['IDENTIFIER', 'lambda'],
|
186
|
+
['LPAREN', '('],
|
187
|
+
['IDENTIFIER', 'r'],
|
188
|
+
['RPAREN', ')'],
|
189
|
+
['LPAREN', '('],
|
190
|
+
['IDENTIFIER', '*'],
|
191
|
+
['IDENTIFIER', 'pi'],
|
192
|
+
['LPAREN', '('],
|
193
|
+
['IDENTIFIER', '*'],
|
194
|
+
['IDENTIFIER', 'r'],
|
195
|
+
['IDENTIFIER', 'r'],
|
196
|
+
['RPAREN', ')'],
|
197
|
+
['RPAREN', ')'],
|
198
|
+
['RPAREN', ')'],
|
199
|
+
['RPAREN', ')']
|
200
|
+
]
|
201
|
+
match_expectations(subject, predicted)
|
156
202
|
end
|
157
203
|
end # context
|
158
204
|
end # describe
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skeem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -85,10 +85,13 @@ files:
|
|
85
85
|
- Rakefile
|
86
86
|
- appveyor.yml
|
87
87
|
- lib/skeem.rb
|
88
|
+
- lib/skeem/grammar.rb
|
89
|
+
- lib/skeem/parser.rb
|
88
90
|
- lib/skeem/stoken.rb
|
89
91
|
- lib/skeem/tokenizer.rb
|
90
92
|
- lib/skeem/version.rb
|
91
93
|
- skeem.gemspec
|
94
|
+
- spec/skeem/parser_spec.rb
|
92
95
|
- spec/skeem/tokenizer_spec.rb
|
93
96
|
- spec/skeem_spec.rb
|
94
97
|
- spec/spec_helper.rb
|
@@ -119,5 +122,6 @@ specification_version: 4
|
|
119
122
|
summary: Skeem is an interpreter of a subset of the Scheme programming language. Scheme
|
120
123
|
is a descendent of the Lisp language.
|
121
124
|
test_files:
|
125
|
+
- spec/skeem/parser_spec.rb
|
122
126
|
- spec/skeem/tokenizer_spec.rb
|
123
127
|
- spec/skeem_spec.rb
|