campa 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +37 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +5 -0
  7. data/Gemfile +15 -0
  8. data/Gemfile.lock +82 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +39 -0
  11. data/Rakefile +23 -0
  12. data/bin/console +15 -0
  13. data/bin/setup +8 -0
  14. data/campa.gemspec +34 -0
  15. data/campa/core.cmp +59 -0
  16. data/campa/example.cmp +2 -0
  17. data/campa/test.cmp +2 -0
  18. data/exe/campa +7 -0
  19. data/lib/campa.rb +18 -0
  20. data/lib/campa/cli.rb +66 -0
  21. data/lib/campa/context.rb +42 -0
  22. data/lib/campa/core/load.rb +25 -0
  23. data/lib/campa/core/print.rb +20 -0
  24. data/lib/campa/core/print_ln.rb +17 -0
  25. data/lib/campa/core/test.rb +52 -0
  26. data/lib/campa/core/test_report.rb +59 -0
  27. data/lib/campa/error/arity.rb +11 -0
  28. data/lib/campa/error/illegal_argument.rb +9 -0
  29. data/lib/campa/error/invalid_number.rb +9 -0
  30. data/lib/campa/error/missing_delimiter.rb +9 -0
  31. data/lib/campa/error/not_a_function.rb +9 -0
  32. data/lib/campa/error/not_found.rb +9 -0
  33. data/lib/campa/error/parameters.rb +11 -0
  34. data/lib/campa/error/reserved.rb +11 -0
  35. data/lib/campa/error/resolution.rb +9 -0
  36. data/lib/campa/evaler.rb +106 -0
  37. data/lib/campa/execution_error.rb +3 -0
  38. data/lib/campa/lambda.rb +45 -0
  39. data/lib/campa/language.rb +33 -0
  40. data/lib/campa/lisp/atom.rb +14 -0
  41. data/lib/campa/lisp/cadr.rb +41 -0
  42. data/lib/campa/lisp/car.rb +22 -0
  43. data/lib/campa/lisp/cdr.rb +22 -0
  44. data/lib/campa/lisp/cond.rb +50 -0
  45. data/lib/campa/lisp/cons.rb +23 -0
  46. data/lib/campa/lisp/core.rb +35 -0
  47. data/lib/campa/lisp/defun.rb +36 -0
  48. data/lib/campa/lisp/eq.rb +9 -0
  49. data/lib/campa/lisp/label.rb +29 -0
  50. data/lib/campa/lisp/lambda_fn.rb +33 -0
  51. data/lib/campa/lisp/list_fn.rb +9 -0
  52. data/lib/campa/lisp/quote.rb +13 -0
  53. data/lib/campa/list.rb +83 -0
  54. data/lib/campa/node.rb +17 -0
  55. data/lib/campa/printer.rb +70 -0
  56. data/lib/campa/reader.rb +198 -0
  57. data/lib/campa/repl.rb +75 -0
  58. data/lib/campa/symbol.rb +23 -0
  59. data/lib/campa/version.rb +5 -0
  60. metadata +119 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 65c3985813d98dec3df69c8f956d3fc06fc2c8c4b48875d8ff1f273a324b83d4
4
+ data.tar.gz: a1563601fdb1e7cb7e9727a80670d51c11704c91595e6cc22184b25a79b89975
5
+ SHA512:
6
+ metadata.gz: 39d8e5da9a5bda8b42cb6e2ec03fed4d2f49eceaaf1f17b8deab6dd903885b79b212c42ba05910ebe253694e63aa42488c74740be6807422792caaf7a420aa26
7
+ data.tar.gz: f944df966fbdd2cbba599c876bc7536f3181aef0d016db96436987737dff9e8f83a95490cdfcc55ac344b6120aed20a741b9e4168dafba7fdda28074095fa715
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,37 @@
1
+ require:
2
+ - rubocop-rake
3
+ - rubocop-rspec
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 3.0
7
+
8
+ Style/StringLiterals:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Style/StringLiteralsInInterpolation:
13
+ Enabled: true
14
+ EnforcedStyle: double_quotes
15
+
16
+ Layout/LineLength:
17
+ Max: 120
18
+
19
+ # Sorry world.
20
+ Style/FrozenStringLiteralComment:
21
+ Enabled: false
22
+
23
+ Style/Documentation:
24
+ Enabled: false
25
+
26
+ Metrics/BlockLength:
27
+ Exclude:
28
+ - spec/**/*_spec.rb
29
+
30
+ Style/NegatedIf:
31
+ Enabled: false
32
+
33
+ Style/TrailingCommaInArrayLiteral:
34
+ Enabled: false
35
+
36
+ Style/TrailingCommaInHashLiteral:
37
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 3.0.1
6
+ install:
7
+ - gem install bundler -v 2.2.20
8
+ - bundle install --jobs=3 --retry=3
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-06-14
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rake", "~> 13.0"
8
+ gem "rspec", "~> 3.0"
9
+
10
+ gem "rubocop", "~> 1.7"
11
+ gem "rubocop-rake", require: false
12
+ gem "rubocop-rspec", require: false
13
+
14
+ gem "pry-byebug"
15
+ gem "simplecov", require: false
data/Gemfile.lock ADDED
@@ -0,0 +1,82 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ campa (0.1.0)
5
+ zeitwerk (~> 2.4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ byebug (11.1.3)
12
+ coderay (1.1.3)
13
+ diff-lcs (1.4.4)
14
+ docile (1.4.0)
15
+ method_source (1.0.0)
16
+ parallel (1.20.1)
17
+ parser (3.0.1.1)
18
+ ast (~> 2.4.1)
19
+ pry (0.13.1)
20
+ coderay (~> 1.1)
21
+ method_source (~> 1.0)
22
+ pry-byebug (3.9.0)
23
+ byebug (~> 11.0)
24
+ pry (~> 0.13.0)
25
+ rainbow (3.0.0)
26
+ rake (13.0.3)
27
+ regexp_parser (2.1.1)
28
+ rexml (3.2.5)
29
+ rspec (3.10.0)
30
+ rspec-core (~> 3.10.0)
31
+ rspec-expectations (~> 3.10.0)
32
+ rspec-mocks (~> 3.10.0)
33
+ rspec-core (3.10.1)
34
+ rspec-support (~> 3.10.0)
35
+ rspec-expectations (3.10.1)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.10.0)
38
+ rspec-mocks (3.10.2)
39
+ diff-lcs (>= 1.2.0, < 2.0)
40
+ rspec-support (~> 3.10.0)
41
+ rspec-support (3.10.2)
42
+ rubocop (1.16.1)
43
+ parallel (~> 1.10)
44
+ parser (>= 3.0.0.0)
45
+ rainbow (>= 2.2.2, < 4.0)
46
+ regexp_parser (>= 1.8, < 3.0)
47
+ rexml
48
+ rubocop-ast (>= 1.7.0, < 2.0)
49
+ ruby-progressbar (~> 1.7)
50
+ unicode-display_width (>= 1.4.0, < 3.0)
51
+ rubocop-ast (1.7.0)
52
+ parser (>= 3.0.1.1)
53
+ rubocop-rake (0.5.1)
54
+ rubocop
55
+ rubocop-rspec (2.4.0)
56
+ rubocop (~> 1.0)
57
+ rubocop-ast (>= 1.1.0)
58
+ ruby-progressbar (1.11.0)
59
+ simplecov (0.21.2)
60
+ docile (~> 1.1)
61
+ simplecov-html (~> 0.11)
62
+ simplecov_json_formatter (~> 0.1)
63
+ simplecov-html (0.12.3)
64
+ simplecov_json_formatter (0.1.3)
65
+ unicode-display_width (2.0.0)
66
+ zeitwerk (2.4.2)
67
+
68
+ PLATFORMS
69
+ x86_64-darwin-19
70
+
71
+ DEPENDENCIES
72
+ campa!
73
+ pry-byebug
74
+ rake (~> 13.0)
75
+ rspec (~> 3.0)
76
+ rubocop (~> 1.7)
77
+ rubocop-rake
78
+ rubocop-rspec
79
+ simplecov
80
+
81
+ BUNDLED WITH
82
+ 2.2.20
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Ricardo Valeriano
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Campa
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/campa`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'campa'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install campa
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ 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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mistersourcerer/campa.
36
+
37
+ ## License
38
+
39
+ 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,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ require "rubocop/rake_task"
8
+ RuboCop::RakeTask.new
9
+
10
+ require "campa"
11
+ desc "executes the campa language tests"
12
+ task :campatest do
13
+ Campa::Cli.new.execute([
14
+ "test",
15
+ Dir.glob(Campa.root.join("../test/**/*").to_s)
16
+ ].flatten)
17
+ rescue SystemExit => e
18
+ # Do not interrupt the rake execution
19
+ # if campa tests were successfull
20
+ exit(1) if e.status != 0
21
+ end
22
+
23
+ task default: %i[spec rubocop campatest]
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 "campa"
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/campa.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/campa/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "campa"
7
+ spec.version = Campa::VERSION
8
+ spec.authors = ["Ricardo Valeriano"]
9
+ spec.email = ["ricardo.valeriano@gmail.com"]
10
+
11
+ spec.summary = "A tiny lispey type of thing for learning purposes."
12
+ spec.description = "A tiny lispey type of thing for learning purposes."
13
+ spec.homepage = "https://github.com/mistersourcerer/campa"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/mistersourcerer/campa."
19
+ spec.metadata["changelog_uri"] = "https://github.com/mistersourcerer/campa/blob/main/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "zeitwerk", "~> 2.4"
31
+
32
+ # For more information and examples about making a new gem, checkout our
33
+ # guide at: https://bundler.io/guides/creating_gem.html
34
+ end
data/campa/core.cmp ADDED
@@ -0,0 +1,59 @@
1
+ (defun null. (x)
2
+ (eq x '()))
3
+
4
+ (defun and. (x y)
5
+ (cond (x (cond (y true) (true '())))))
6
+
7
+ (defun not. (x)
8
+ (cond (x '())
9
+ (true true)))
10
+
11
+ (defun append. (x y)
12
+ (cond ((null. x) y)
13
+ (true (cons (car x) (append. (cdr x) y)))))
14
+
15
+ (defun pair. (x y)
16
+ (cond ((and. (null. x) (null. y)) '())
17
+ ((and. (not. (atom x)) (not. (atom y)))
18
+ (cons (list (car x) (car y))
19
+ (pair. (cdr x) (cdr y))))))
20
+
21
+ (defun assoc. (x y)
22
+ (cond ((eq y '()) '())
23
+ ((eq (caar y) x) (cadar y))
24
+ (true (assoc. x (cdr y)))))
25
+
26
+ (defun evcon. (c a)
27
+ (cond ((eval. (caar c) a)
28
+ (eval. (cadar c) a))
29
+ (true (evcon. (cdr c) a))))
30
+
31
+ (defun evlis. (m a)
32
+ (cond ((null. m) '())
33
+ (true (cons (eval. (car m) a)
34
+ (evlis. (cdr m) a)))))
35
+
36
+ (defun eval. (e a)
37
+ (cond
38
+ ((atom e) (assoc. e a))
39
+ ((atom (car e))
40
+ (cond
41
+ ((eq (car e) 'quote) (cadr e))
42
+ ((eq (car e) 'atom) (atom (eval. (cadr e) a)))
43
+ ((eq (car e) 'eq) (eq (eval. (cadr e) a)
44
+ (eval. (caddr e) a)))
45
+ ((eq (car e) 'car) (car (eval. (cadr e) a)))
46
+ ((eq (car e) 'cdr) (cdr (eval. (cadr e) a)))
47
+ ((eq (car e) 'cons) (cons (eval. (cadr e) a)
48
+ (eval. (caddr e) a)))
49
+ ((eq (car e) 'cond) (evcon. (cdr e) a))
50
+ (true (eval. (cons (assoc. (car e) a)
51
+ (cdr e))
52
+ a))))
53
+ ((eq (caar e) 'label)
54
+ (eval. (cons (caddar e) (cdr e))
55
+ (cons (list (cadar e) (car e)) a)))
56
+ ((eq (caar e) 'lambda)
57
+ (eval. (caddar e)
58
+ (append. (pair. (cadar e) (evlis. (cdr e) a))
59
+ a)))))
data/campa/example.cmp ADDED
@@ -0,0 +1,2 @@
1
+ (defun hello-world () (print "hello world"))
2
+ (hello-world)
data/campa/test.cmp ADDED
@@ -0,0 +1,2 @@
1
+ (defun assert (x y)
2
+ (eq x y))
data/exe/campa ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "campa"
4
+
5
+ Campa::Cli
6
+ .new
7
+ .execute(ARGV)
data/lib/campa.rb ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "campa/version"
4
+ require "zeitwerk"
5
+
6
+ loader = Zeitwerk::Loader.for_gem
7
+ loader.setup
8
+
9
+ module Campa
10
+ CR_REGEX = /\Ac((ad)|(a|d){2,})r$$/ # caar, cddr, cadr, but not car or cdr
11
+ SYMBOL_OUT = Symbol.new("__out__")
12
+ SYMBOL_LAMBDA = Symbol.new("lambda")
13
+ SYMBOL_QUOTE = Symbol.new("quote")
14
+
15
+ def self.root
16
+ @root ||= Pathname.new File.expand_path(__dir__)
17
+ end
18
+ end
data/lib/campa/cli.rb ADDED
@@ -0,0 +1,66 @@
1
+ module Campa
2
+ class Cli
3
+ def initialize(repl: nil, evaler: nil, context: nil, reader: nil)
4
+ @evaler = evaler || default_evaler
5
+ @context = context || default_context
6
+ @reader = reader || default_reader
7
+
8
+ @repl = repl || default_repl
9
+ end
10
+
11
+ def execute(argv = nil, input: $stdin, out: $stdout)
12
+ return repl.run(input, out) if argv.nil? || argv.empty?
13
+ return evaluate(argv[0], input, out) if File.file?(argv[0])
14
+
15
+ execute_option(argv[0].to_sym, argv[1..], input, out)
16
+ end
17
+
18
+ private
19
+
20
+ OPTIONS = {
21
+ test: :test,
22
+ }.freeze
23
+
24
+ attr_reader :repl, :evaler, :context, :reader
25
+
26
+ def new_eval_ctx(out)
27
+ context.push(SYMBOL_OUT => out)
28
+ end
29
+
30
+ def evaluate(file, _, out)
31
+ evaler.eval(reader.new(file), new_eval_ctx(out))
32
+ exit(0)
33
+ end
34
+
35
+ def execute_option(option, args, input, out)
36
+ method = OPTIONS.fetch(option) { raise "Unknown option #{option}" }
37
+ method(method).call(args, input, out)
38
+ end
39
+
40
+ def test(files, _, out)
41
+ eval_ctx = new_eval_ctx(out)
42
+ files.each { |file| evaler.eval(reader.new(file), eval_ctx) }
43
+ results = Campa::Core::Test.new.call(env: eval_ctx)
44
+ reporter = Campa::Core::TestReport.new
45
+ code = reporter.call(results, env: eval_ctx) ? 0 : 1
46
+
47
+ exit(code)
48
+ end
49
+
50
+ def default_repl
51
+ @default_repl ||= Campa::Repl.new(evaler, context)
52
+ end
53
+
54
+ def default_evaler
55
+ @default_evaler ||= Campa::Evaler.new
56
+ end
57
+
58
+ def default_context
59
+ @default_context ||= Campa::Language.new
60
+ end
61
+
62
+ def default_reader
63
+ @default_reader ||= Campa::Reader
64
+ end
65
+ end
66
+ end