rbsiev 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '09e27f55acd8c832a0e6feeb288bfa7ab7c8201a2825a1d008f0e387b47e9a8d'
4
+ data.tar.gz: f2cd0be2bf4928fbfe8f27c671048fa416fac1a4d174121bdd8e1ec1188fc63e
5
+ SHA512:
6
+ metadata.gz: 1d006ac8919f712ce78ac63553121a82951300b2785e2f853f7d1c3a5df677ac38916be289478e42a597caf777938c6a1ef24e775f76eeb93abce74ed720f232
7
+ data.tar.gz: e7f84522ead7e7c8807866f58c86bdfa9ee64e42a7d348e91bc33c75fa818bcd2432ea78fff68b5087a3257af42bc248cdd38ce67efc25fb2404883dd2ed7c23
@@ -0,0 +1,18 @@
1
+ name: Build
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 3.0.1
14
+ - name: Run the default task
15
+ run: |
16
+ gem install bundler -v 2.2.15
17
+ bundle install
18
+ bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,57 @@
1
+ Gemfile.lock
2
+ *.gem
3
+ *.rbc
4
+ /.config
5
+ /coverage/
6
+ /InstalledFiles
7
+ /pkg/
8
+ /spec/reports/
9
+ /spec/examples.txt
10
+ /test/tmp/
11
+ /test/version_tmp/
12
+ /tmp/
13
+
14
+ # Used by dotenv library to load environment variables.
15
+ # .env
16
+
17
+ # Ignore Byebug command history file.
18
+ .byebug_history
19
+
20
+ ## Specific to RubyMotion:
21
+ .dat*
22
+ .repl_history
23
+ build/
24
+ *.bridgesupport
25
+ build-iPhoneOS/
26
+ build-iPhoneSimulator/
27
+
28
+ ## Specific to RubyMotion (use of CocoaPods):
29
+ #
30
+ # We recommend against adding the Pods directory to your .gitignore. However
31
+ # you should judge for yourself, the pros and cons are mentioned at:
32
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
33
+ #
34
+ # vendor/Pods/
35
+
36
+ ## Documentation cache and generated files:
37
+ /.yardoc/
38
+ /_yardoc/
39
+ /doc/
40
+ /rdoc/
41
+
42
+ ## Environment normalization:
43
+ /.bundle/
44
+ /vendor/bundle
45
+ /lib/bundler/man/
46
+
47
+ # for a library or gem, you might want to ignore these files since the code is
48
+ # intended to run in multiple environments; otherwise, check them in:
49
+ # Gemfile.lock
50
+ # .ruby-version
51
+ # .ruby-gemset
52
+
53
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
54
+ .rvmrc
55
+
56
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
57
+ # .rubocop-https?--*
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-06-08
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in rbsiev.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 mnbi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Rbsiev
2
+
3
+ [![Build Status](https://github.com/mnbi/rbsiev/workflows/Build/badge.svg)](https://github.com/mnbi/rbsiev/actions?query=workflow%3A"Build")
4
+
5
+ SICP evaluator in Ruby.
6
+
7
+ Or, Ruby で書いた SICP の evaluator.
8
+
9
+ It is intended to be a part of Scheme implementation written in Ruby.
10
+
11
+ ## Installation
12
+
13
+ Execute:
14
+
15
+ $ gem install rbsiev
16
+
17
+ ## Usage
18
+
19
+ Run the simple REPL as:
20
+
21
+ $ rbsiev
22
+
23
+ Then, you can input Scheme program.
24
+
25
+ ## Supported Syntax
26
+
27
+ In 0.1.0, the evaluator mostly corresponds to the one in SICP 4.1.1 -
28
+ 4.1.6.
29
+
30
+ See the [wiki page](https://github.com/mnbi/rbsiev/wiki/実装の進捗)
31
+ for more information about the progress of implementing.
32
+
33
+ ### Literals
34
+
35
+ - boolean value (`#f` and `#t`),
36
+ - an empty list (`()`),
37
+ - a string enclosing with double-quotations, such `"hoge"`
38
+ - numbers (integer, real, rational, and complex)
39
+
40
+ ### Primitive procedures
41
+
42
+ - arithmetic operators (`+`, `-`, `*`, `/`, `%`),
43
+ - comparison operators (`=`, `<`, `>`, `<=`, `>=`)
44
+ - `cons`, `car`, `cdr`
45
+ - `null?`, `pair?`, `list?`, `number?`
46
+ - `list`, `append`
47
+ - `write`, `display`
48
+
49
+ ### Syntax to be evaluated
50
+
51
+ - lambda expression
52
+ - such `(lambda (n) (+ n 1))`
53
+ - procedure application
54
+ - such `(foo 1)`
55
+ - conditional expression
56
+ - `if`, `cond`, `when`, `unless`
57
+ - logical test
58
+ - `and`, `or`
59
+ - assignment
60
+ - `set!`
61
+ - definition
62
+ - `define`
63
+ - sequence
64
+ - `begin`
65
+ - local bindings
66
+ - `let`, `let*`, `letrec`, `letrec*`
67
+ - loop
68
+ - `do`
69
+
70
+ ## Related projects
71
+
72
+ - Lexical Analyzer for Scheme - [mnbi/rbscmlex](https://github.com/mnbi/rbscmlex)
73
+ - Abstract Syntax Tree and Syntax Analyzer for Scheme - [mnbi/rubasteme](https://github.com/mnbi/rubasteme)
74
+ - Ruby wit Syntax Sugar of Scheme - [mnbi/rus3](https://github.com/mnbi/rus3)
75
+ - A small Scheme implementation in Ruby - [mnbi/rubeme](https://github.com/mnbi/rubeme)
76
+
77
+ ## Development
78
+
79
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
80
+
81
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
82
+
83
+ ## Contributing
84
+
85
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/mnbi/rbsiev](https://github.com/mnbi/rbsiev).
86
+
87
+
88
+ ## License
89
+
90
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
13
+
14
+ require "rdoc/task"
15
+
16
+ RDoc::Task.new do |rdoc|
17
+ rdoc.generator = "ri"
18
+ rdoc.rdoc_dir = "rdoc"
19
+ rdoc.rdoc_files.include("lib/**/*.rb")
20
+ end
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "rbsiev"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/rbsiev ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rbsiev"
4
+
5
+ def usage
6
+ puts <<HELP
7
+ usage:
8
+ rbsiev [option] [FILE]
9
+ option:
10
+ -d, --debug : specify to run in verbose mode
11
+ -v, --version : print version
12
+ -h, --help : show this message
13
+ HELP
14
+ end
15
+
16
+ def print_version
17
+ ver = Rbsiev::VERSION
18
+ rel = Rbsiev::RELEASE
19
+ puts "(rbsiev :version #{ver} :release #{rel})"
20
+ end
21
+
22
+ opts = {}
23
+
24
+ while ARGV.size > 0
25
+ arg = ARGV.shift
26
+ case arg
27
+ when "-d", "--debug"
28
+ opts[:verbose] = true
29
+ when "-v", "--version"
30
+ print_version
31
+ exit 0
32
+ when "-h", "--help"
33
+ usage
34
+ exit 0
35
+ else
36
+ opts[:files] ||= []
37
+ opts[:files] << arg
38
+ end
39
+ end
40
+
41
+ begin
42
+ if opts[:files]
43
+ Rbsiev.run(**opts)
44
+ else
45
+ opts[:prompt] = opts[:verbose] ? "rbsiev[#{Rbsiev::VERSION}]> " : "rbsiev> "
46
+ Rbsiev::Repl.start(**opts)
47
+ end
48
+ rescue Rbsiev::Error => e
49
+ puts e.message
50
+ exit 1
51
+ end
data/lib/rbsiev.rb ADDED
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbscmlex"
4
+ require "rubasteme"
5
+ require_relative "rubasteme/ast/misc"
6
+ require_relative "scmo/object"
7
+
8
+ module Rbsiev
9
+ require_relative "rbsiev/version"
10
+ require_relative "rbsiev/error"
11
+ require_relative "rbsiev/primitives"
12
+ require_relative "rbsiev/procedure"
13
+ require_relative "rbsiev/environment"
14
+ require_relative "rbsiev/evaluator"
15
+ require_relative "rbsiev/printer"
16
+ require_relative "rbsiev/repl"
17
+
18
+ SCM_EMPTY_LIST = Scmo::EMPTY_LIST
19
+ SCM_TRUE = Scmo::TRUE
20
+ SCM_FALSE = Scmo::FALSE
21
+
22
+ def self.scheme_object?(obj)
23
+ Scmo.scheme_object?(obj)
24
+ end
25
+
26
+ Components = Struct.new(:parser, :evaluator, :printer, :env) {
27
+ def parse(source)
28
+ parser.parse(Rbscmlex.lexer(source))
29
+ end
30
+
31
+ def eval(ast_node)
32
+ evaluator.eval(ast_node, env)
33
+ end
34
+
35
+ def print(value)
36
+ printer.print(value)
37
+ end
38
+
39
+ def exec(source)
40
+ self.print(self.eval(parse(source)))
41
+ end
42
+
43
+ def load(file)
44
+ raise Error, "Cannot find \"%s\"" % f unless FileTest.exist?(file)
45
+ source = File.readlines(file, chomp: true).join(" ")
46
+ self.exec(source)
47
+ end
48
+
49
+ def version
50
+ comp_vers = []
51
+ comp_vers << Rbscmlex::Lexer.version
52
+ comp_vers << Rubasteme::Parser.version
53
+ comp_vers << Evaluator.version
54
+ comp_vers.join("\n")
55
+ end
56
+ }
57
+
58
+ def self.evaluator
59
+ Evaluator.new
60
+ end
61
+
62
+ def self.printer
63
+ Printer.new
64
+ end
65
+
66
+ def self.setup_environment
67
+ initial_env = Environment.the_empty_environment
68
+ names = []
69
+ values = []
70
+ PRIMITIVE_NAMES_MAP.each { |name, sym|
71
+ names << name
72
+ values << Procedure.make_procedure(nil, sym, initial_env)
73
+ }
74
+ initial_env.extend(names, values)
75
+ end
76
+
77
+ def self.eval(scm_source)
78
+ lexer = Rbscmlex.lexer(scm_source)
79
+ parser = Rubasteme.parser
80
+ eva = evaluator
81
+ env = setup_environment
82
+ eva.eval(parser.parse(lexer), env)
83
+ end
84
+
85
+ def self.run(files:, verbose: false)
86
+ parser = Rubasteme.parser
87
+ evaluator = Evaluator.new
88
+ env = setup_environment
89
+ printer = Printer.new
90
+
91
+ evaluator.verbose = verbose
92
+ printer.verbose = verbose
93
+
94
+ components = Components.new(parser, evaluator, printer, env)
95
+
96
+ files.each {|f| components.load(f)}
97
+ end
98
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbsiev
4
+
5
+ class Environment
6
+ include Primitives
7
+
8
+ def self.the_empty_environment
9
+ Environment.new(nil)
10
+ end
11
+
12
+ def self.make_frame(variables, values)
13
+ Frame.new(variables, values)
14
+ end
15
+
16
+ class Frame
17
+ def initialize(variables = [], values = [])
18
+ @bindings = variables.zip(values).to_h
19
+ end
20
+
21
+ def variables
22
+ @bindings.keys
23
+ end
24
+
25
+ def values
26
+ @bindings.values
27
+ end
28
+
29
+ def lookup(var)
30
+ @bindings[var]
31
+ end
32
+
33
+ def add_binding(var, val)
34
+ @bindings.merge!({var => val})
35
+ end
36
+
37
+ def set(var, value)
38
+ @bindings[var] = value
39
+ end
40
+ end
41
+
42
+ def initialize(base_env)
43
+ @frame = nil
44
+ @enclosing_environment = base_env
45
+ end
46
+
47
+ attr_reader :enclosing_environment
48
+
49
+ def first_frame
50
+ @frame
51
+ end
52
+
53
+ def extend(vars, vals)
54
+ if vars.size == vals.size
55
+ new_env = Environment.new(self)
56
+ new_env.set_frame!(Environment.make_frame(vars, vals))
57
+ new_env
58
+ elsif var.size < vals.size
59
+ raise Error, "Too many arguments supplied: %s => %s" % [vars, vals]
60
+ else
61
+ raise Error, "Too few arguments supplied: %s => %s" % [vars, vals]
62
+ end
63
+ end
64
+
65
+ def lookup_variable_value(var)
66
+ val = nil
67
+ env = self
68
+ while env
69
+ val = env.first_frame && env.first_frame.lookup(var)
70
+ break if val
71
+ env = env.enclosing_environment
72
+ end
73
+ raise Error, "Unbound variable: got=%s" % var if val.nil?
74
+ val
75
+ end
76
+
77
+ def define_variable(var, val)
78
+ if first_frame.nil?
79
+ set_frame!(Environment.make_frame([var], [val]))
80
+ else
81
+ first_frame.add_binding(var, val)
82
+ end
83
+ var
84
+ end
85
+
86
+ def set_variable_value(var, val)
87
+ current = nil
88
+ env = self
89
+ while env
90
+ current = env.first_frame && env.first_frame.lookup(var)
91
+ if current
92
+ env.first_frame.set(var, val)
93
+ break
94
+ else
95
+ env = env.enclosing_environment
96
+ end
97
+ end
98
+ raise Error, "Unbound variable: got=%s" % var if current.nil?
99
+ val
100
+ end
101
+
102
+ protected
103
+
104
+ def set_frame!(frame)
105
+ @frame = frame
106
+ end
107
+
108
+ end # end of Environment
109
+
110
+ end