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 +4 -4
- data/.travis.yml +6 -0
- data/Gemfile.lock +1 -1
- data/README.md +228 -12
- data/campa.gemspec +1 -4
- data/lib/campa/core/load.rb +3 -2
- data/lib/campa/core/print.rb +1 -1
- data/lib/campa/core/print_ln.rb +1 -1
- data/lib/campa/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0150f5fd77bacb8efd9e190bdae9ba219db3fbd4aea1cda44677dd97c5c33881
|
4
|
+
data.tar.gz: 7a68bda264a3ccb37c74c618c1c38e472d0786efc90a335065f66c640c00892c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/README.md
CHANGED
@@ -1,28 +1,244 @@
|
|
1
1
|
# Campa
|
2
2
|
|
3
|
-
|
3
|
+

|
4
|
+

|
5
|
+

|
4
6
|
|
5
|
-
|
7
|
+
This is a [LISP](https://www.britannica.com/technology/LISP-computer-language) implementation written in Ruby.
|
6
8
|
|
7
|
-
|
9
|
+
[](https://xkcd.com/297/)
|
8
10
|
|
9
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
+
You can install this gem normaly with:
|
15
|
+
|
16
|
+
$ gem install campa
|
14
17
|
|
15
|
-
And
|
18
|
+
And after that the REPL can be run:
|
16
19
|
|
17
|
-
$
|
20
|
+
$ campa
|
18
21
|
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/campa/core/load.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|
data/lib/campa/core/print.rb
CHANGED
data/lib/campa/core/print_ln.rb
CHANGED
data/lib/campa/version.rb
CHANGED
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.
|
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-
|
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: []
|