skeem 0.0.2 → 0.0.3

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: 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