campa 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![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
|
-
|
7
|
+
This is a [LISP](https://www.britannica.com/technology/LISP-computer-language) implementation written in Ruby.
|
6
8
|
|
7
|
-
|
9
|
+
[![XKCD lisp cycles comic strip](https://imgs.xkcd.com/comics/lisp_cycles.png)](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: []
|