campa 0.1.0 → 0.1.1

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
  SHA256:
3
- metadata.gz: 65c3985813d98dec3df69c8f956d3fc06fc2c8c4b48875d8ff1f273a324b83d4
4
- data.tar.gz: a1563601fdb1e7cb7e9727a80670d51c11704c91595e6cc22184b25a79b89975
3
+ metadata.gz: 0150f5fd77bacb8efd9e190bdae9ba219db3fbd4aea1cda44677dd97c5c33881
4
+ data.tar.gz: 7a68bda264a3ccb37c74c618c1c38e472d0786efc90a335065f66c640c00892c
5
5
  SHA512:
6
- metadata.gz: 39d8e5da9a5bda8b42cb6e2ec03fed4d2f49eceaaf1f17b8deab6dd903885b79b212c42ba05910ebe253694e63aa42488c74740be6807422792caaf7a420aa26
7
- data.tar.gz: f944df966fbdd2cbba599c876bc7536f3181aef0d016db96436987737dff9e8f83a95490cdfcc55ac344b6120aed20a741b9e4168dafba7fdda28074095fa715
6
+ metadata.gz: d73fd6d7e091d1c2820ead91af26e5d7ce94cf8971c08829bf4a8323c150b589bd0505f3cfde2df34d263259c168284413b4e9399213ca85b164ddfbc416aa6e
7
+ data.tar.gz: 93c832a4259a5ce546bfea743b6a0b1f0f02667dec51b9dbffab796b8ec13fa0b0a56e44bfecf7c392a596a0ae39c2a669a28a04a0016a881629081f9d731f74
data/.travis.yml CHANGED
@@ -6,3 +6,9 @@ rvm:
6
6
  install:
7
7
  - gem install bundler -v 2.2.20
8
8
  - bundle install --jobs=3 --retry=3
9
+ before_script:
10
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
11
+ - chmod +x ./cc-test-reporter
12
+ - ./cc-test-reporter before-build
13
+ after_script:
14
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- campa (0.1.0)
4
+ campa (0.1.1)
5
5
  zeitwerk (~> 2.4)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,28 +1,244 @@
1
1
  # Campa
2
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.
3
+ ![travis build (shildes.io) badge](https://img.shields.io/travis/com/mistersourcerer/campa?style=plastic "Build Status")
4
+ ![coverage (shildes.io) badge](https://img.shields.io/codeclimate/coverage/mistersourcerer/campa?style=plastic "Coverage Status")
5
+ ![gem version (shildes.io) badge](https://img.shields.io/gem/v/campa?include_prereleases&style=plastic "Version")
4
6
 
5
- TODO: Delete this and the text above, and describe your gem
7
+ This is a [LISP](https://www.britannica.com/technology/LISP-computer-language) implementation written in Ruby.
6
8
 
7
- ## Installation
9
+ [![XKCD lisp cycles comic strip](https://imgs.xkcd.com/comics/lisp_cycles.png)](https://xkcd.com/297/)
8
10
 
9
- Add this line to your application's Gemfile:
11
+ It comes equiped with a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)
12
+ and a (very rudimentary) test framework.
10
13
 
11
- ```ruby
12
- gem 'campa'
13
- ```
14
+ You can install this gem normaly with:
15
+
16
+ $ gem install campa
14
17
 
15
- And then execute:
18
+ And after that the REPL can be run:
16
19
 
17
- $ bundle install
20
+ $ campa
18
21
 
19
- Or install it yourself as:
22
+ This will give you a prompt
23
+ where you can check if campa is working
24
+ by making an offer to the gods of programming:
20
25
 
21
- $ gem install campa
26
+ => (defun hello-world () (print "hello world"))
27
+ (lambda () (print "hello world"))
28
+ => (hello-world)
29
+ "hello world"NIL
30
+
31
+ ## What is this about?
32
+
33
+ Before going any further
34
+ you should probably know that,
35
+ as a LISP implementation,
36
+ this one is for sure on the EXTREMELY simple
37
+ side of the measuring device - whatever such a thing may be.
38
+
39
+ <img src="https://i.chzbgr.com/full/6819818496/h1DC5249B/measurement-fail" width="300" />
40
+
41
+ It's main purpose is to create room
42
+ for the author to learn about LISP
43
+ and language implementation.
44
+ So you should curb your enthusiasm while using/reading it.
45
+
46
+
47
+ ### Paul Graham's The Roots of Lisp
48
+
49
+ There is this article by [Paul Graham](http://www.paulgraham.com/)
50
+ called [The Roots of Lisp](http://www.paulgraham.com/rootsoflisp.html).
51
+ As far as I can tell this is a seminal work
52
+ [that helped many people to understand LISP better](https://hn.algolia.com/?dateRange=all&page=0&prefix=false&query=%22roots%20of%20lisp%22&sort=byPopularity&type=story)
53
+ and maybe even made [the original McCarthy's ideas](https://crypto.stanford.edu/~blynn/lambda/lisp.html)
54
+ more accessible.
55
+
56
+ This project implements **LISP** to the point
57
+ where all the functions exemplified by Graham's article
58
+ run successfully. Which means one could implement *Campa* on itself
59
+ (or they could just [go here](../blob/main/campa/core.cmp)
60
+ and see how it is done already).
22
61
 
23
62
  ## Usage
24
63
 
25
- TODO: Write usage instructions here
64
+ ### Executing .cmp files
65
+
66
+ Let's say you have a *Campa* file `hello.cmp`
67
+ with the following code:
68
+
69
+ ```lisp
70
+ (defun hello (stuff) (print "hello" stuff))
71
+ (label thing "Marvin")
72
+
73
+ (hello thing)
74
+ ```
75
+
76
+ You can run this code
77
+ by using the *campa* executable like this:
78
+
79
+ $ campa hello.cmp
80
+ hello Marvin
81
+
82
+ Note that the functions *print* and *println*
83
+ are given "for free" to the user
84
+ in the sense that they are not specified in the *Roots of Lisp*.
85
+ But more on that later.
86
+
87
+ ### Playing around in the REPL
88
+
89
+ Now if you want to play around with some ideas
90
+ before commit those to a file
91
+ (or version control, for that matter)
92
+ you can fire up the repl by typing _campa_:
93
+
94
+ $ campa
95
+ =>
96
+
97
+ The **=>** is the *REPL* prompt
98
+ and a sign that you can enter code now.
99
+
100
+ I will use this opportunity to show you
101
+ yet another function that comes with this implementation
102
+ but is not part of the *Roots of Lisp*.
103
+ Let's *load* the same file that was used on the previous example
104
+ in the current *REPL* session.
105
+
106
+ => (load "./hello.cmp")
107
+ hello MarvinNIL
108
+
109
+ The **NIL** shown after the printed message
110
+ (hello Marvin) is the return of the *print* function itself.
111
+
112
+ Notice also that the function *hello*,
113
+ declared in the *hello.cmp* file,
114
+ is now available in the current *REPL* session.
115
+
116
+ => (hello "World")
117
+ hello WorldNIL
118
+
119
+ ## The Roots
120
+
121
+ The following functions are available
122
+ and considered the **core** of this LISP implementation.
123
+ They all have the expected behavior specified
124
+ in the aforementioned article [*The Roots of Lisp*](http://www.paulgraham.com/rootsoflisp.html).
125
+
126
+ ```lisp
127
+ (atom thing)
128
+ (car list)
129
+ (cdr list)
130
+ (cond ((condition) some-value) ((other-condition) other-value))
131
+ (cons 'new-head '(list with stuffz))
132
+ (defun func-name (params, more-params) (do-something))
133
+ (eq 'meaning-of-life 42)
134
+ (label a-symbol 4,20)
135
+ (lambda (params, more-params) (do-something))
136
+ (list 'this 'creates 'a 'list 'with 'all 'given 'params)
137
+ (quote '(a b c))
138
+ ```
139
+
140
+ Besides the actual functions
141
+ the notation that uses single quotation mark (')
142
+ to quote an object is also implemented in the runtime.
143
+
144
+ ### Implementation details
145
+
146
+ All those functions are implemented in Ruby
147
+ [and they live right here](../blob/4fc244d41dd6d9/lib/campa/lisp).
148
+
149
+ ## Beyond the Roots
150
+
151
+ Some mentions were made in this very own Readme
152
+ about things that are available on *Campa*
153
+ but were not specified on *Roots of Lisp*.
154
+
155
+ Those are mainly functions and they come in two flavors:
156
+ the ones implemented on *Campa* itself
157
+ and the ones implemented on the runtime (in Ruby).
158
+
159
+ ### Extras in Campa
160
+
161
+ - [(assert x y)](../blob/4fc244d41dd6d9/campa/test.cmp#L1)
162
+ Returns true if x and y are *eq*.
163
+
164
+ ### Extras in Ruby (runtime)
165
+
166
+ - [(load a-file another-file)](../blob/4fc244d41dd6d9/lib/campa/core/load.rb)
167
+ Read and evaluate the files given as arguments
168
+ - [(print "some" "stuff" 4 '("even" "lists"))](../blob/4fc244d41dd6d9/lib/campa/core/print.rb)
169
+ Print out a reader friendly representation of the given parameter(s).
170
+ - [(println stuffz here)](../blob/4fc244d41dd6d9/lib/campa/core/println.rb)
171
+ Same as print but add a line break (`\n`) after each parameter.
172
+
173
+ #### Tests
174
+
175
+ There is a very simplistic test framework
176
+ implemented in the runtime.
177
+ It is available via the *campa* command line
178
+ with the option **test**.
179
+ It receives as argument files containing well...
180
+ tests.
181
+
182
+ The definition of a test in this context is any function
183
+ that starts with *test-* or *test_* (case insentive)
184
+ and return *true* for success
185
+ or *false* for failure.
186
+ The core implementation for *Campa* rely on this tool
187
+ and you can check out [some test examples here](../blob/4fc244d41dd6d9/test/core_test.cmp).
188
+
189
+ $ campa test test/core_test.cmp
190
+
191
+ 10 tests ran
192
+ Success: none of those returned false
193
+
194
+ Internally this "framework" is comprised
195
+ of the two following functions:
196
+
197
+ - [(tests-run optional-name other-optional-name)](../blob/4fc244d41dd6d9/lib/campa/core/test.rb)
198
+ - [(tests-report (tests-run))](../blob/4fc244d41dd6d9/lib/campa/core/test_report.rb)
199
+
200
+ ##### (tests-run)
201
+
202
+ The *tests-run* function will find
203
+ any function whose name starts with *test-* or *test_* (case insensitive)
204
+ in the current context and run it.
205
+ A function that returns false
206
+ is considered a failing test.
207
+
208
+ We can simulate this clearly in the REPL:
209
+
210
+ $ campa
211
+ => (defun test-one () "do nothing" false)
212
+ (lambda () "do nothing" false)
213
+ => (defun test-two () "great success" true)
214
+ (lambda () "great success" true)
215
+ => (tests-run)
216
+ ((success (test-two)) (failures (test-one)))
217
+
218
+ This structure returned by *tests-run*
219
+ is well known by a second function...
220
+
221
+ ##### (tests-report)
222
+
223
+ ... we can use the *tests-report* function
224
+ to show a nice summary of tests ran:
225
+
226
+ => (tests-report (tests-run))
227
+ 2 tests ran
228
+ FAIL!
229
+ 1 tests failed
230
+ they are:
231
+ - test-one
232
+ false
233
+
234
+ Notice that *tests-report* return *false* if there is a failure
235
+ or *true* if all tests pass.
236
+ This is an easy way to integrate this tool
237
+ with CI environments or any type of "progressive" build.
238
+
239
+ An example of how this is used by
240
+ *Campa* implementation [can be found here](../blob/4fc244d41dd6d9/Rakefile#L12).
241
+
26
242
 
27
243
  ## Development
28
244
 
data/campa.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  spec.required_ruby_version = ">= 3.0"
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] = "https://github.com/mistersourcerer/campa."
18
+ spec.metadata["source_code_uri"] = "https://github.com/mistersourcerer/campa"
19
19
  spec.metadata["changelog_uri"] = "https://github.com/mistersourcerer/campa/blob/main/CHANGELOG.md"
20
20
 
21
21
  # Specify which files should be added to the gem when it is released.
@@ -28,7 +28,4 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ["lib"]
29
29
 
30
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
31
  end
@@ -8,7 +8,8 @@ module Campa
8
8
  def call(*paths, env:)
9
9
  verify_presence(paths)
10
10
  paths.reduce(nil) do |_, file|
11
- evaler.eval(Reader.new(file), env)
11
+ reader = Reader.new(File.expand_path(file))
12
+ evaler.eval(reader, env)
12
13
  end
13
14
  end
14
15
 
@@ -17,7 +18,7 @@ module Campa
17
18
  attr_reader :evaler
18
19
 
19
20
  def verify_presence(paths)
20
- not_here = paths.find { |f| !File.exist?(f) }
21
+ not_here = paths.find { |f| !File.file?(File.expand_path(f)) }
21
22
  raise Error::NotFound, not_here if !not_here.nil?
22
23
  end
23
24
  end
@@ -4,7 +4,7 @@ module Campa
4
4
  def call(*stuff, env:)
5
5
  string =
6
6
  stuff
7
- .map { |s| printer.call(s) }
7
+ .map { |s| s.is_a?(String) ? s : printer.call(s) }
8
8
  .join(" ")
9
9
  (env[SYMBOL_OUT] || $stdout).print(string)
10
10
  nil
@@ -3,7 +3,7 @@ module Campa
3
3
  class PrintLn
4
4
  def call(*stuff, env:)
5
5
  out = env[SYMBOL_OUT] || $stdout
6
- stuff.each { |s| out.puts printer.call(s) }
6
+ stuff.each { |s| out.puts(s.is_a?(String) ? s : printer.call(s)) }
7
7
  nil
8
8
  end
9
9
 
data/lib/campa/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Campa
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: campa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ricardo Valeriano
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-27 00:00:00.000000000 Z
11
+ date: 2021-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -95,7 +95,7 @@ licenses:
95
95
  - MIT
96
96
  metadata:
97
97
  homepage_uri: https://github.com/mistersourcerer/campa
98
- source_code_uri: https://github.com/mistersourcerer/campa.
98
+ source_code_uri: https://github.com/mistersourcerer/campa
99
99
  changelog_uri: https://github.com/mistersourcerer/campa/blob/main/CHANGELOG.md
100
100
  post_install_message:
101
101
  rdoc_options: []