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 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: []