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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a4521e994b28e81e3fa1f8852bf096c2e375def5
4
- data.tar.gz: 86e7e0738987b88b9043740632b510e5bee6a49f
3
+ metadata.gz: 670bb82d2fecac647938c03d92b8c910f9385a49
4
+ data.tar.gz: 3ac772b051b36ded30aefe62c6745239c9a1941f
5
5
  SHA512:
6
- metadata.gz: 0197b9df68cba199a243b53d22f329728575ec2c8021b813f6f3eb4be4e39d530cfc53fa221be7994a636da06fdc1ad17977985d7d4e0c54dd3f4dda1c8b6cae
7
- data.tar.gz: 493fb5dd6786dda700abf79ea211d96b832772540986b36eca887ddb2abba4816954080fb5508fdc55dc9ab4b375b5b129f031f507efcf9caf081e05b28ba10f
6
+ metadata.gz: 6b8488389ba2a11fa1664bb6f3e2a40c752a16045061f516360cce2744e9e05e2746d823f8854fa40f23fe24183105384863f19156a7b425a86d95071d2ad9c4
7
+ data.tar.gz: 1c6029799408c83027072cdfca72f9d86dd6bbf5519af149fbb3736e4224f223f553c1463e56fd0461b0e977ae91a2be14ab2a8f4355e9a1ae5449094167a08b
@@ -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
+ [![Linux Build Status](https://travis-ci.org/famished-tiger/Skeem.svg?branch=master)](https://travis-ci.org/famished-tiger/Skeem)
3
+ [![Gem Version](https://badge.fury.io/rb/skeem.svg)](https://badge.fury.io/rb/skeem)
4
+ [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](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, it is too early to use the gem.
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
 
@@ -1,5 +1,8 @@
1
- require 'skeem/version'
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
- module Skeem
4
- # Your code goes here...
5
- end
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
@@ -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
@@ -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
- token = build_token('IDENTIFIER', lexeme)
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|[|()";])|$)/))
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.0.2'.freeze
2
+ VERSION = '0.0.3'.freeze
3
3
  end
@@ -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.2
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-25 00:00:00.000000000 Z
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