lasp 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c3c2a2b3d78d531e4669760374632562219a5114
4
+ data.tar.gz: cea95705d76894736b21d1dac3c24691c606f417
5
+ SHA512:
6
+ metadata.gz: 7162a39c7d3000b3d1e20e93b0386fc9e60f7e1d793eb73a9b966be4c69f217c0a9bb21567649d2d5576700bde1995a85bbf5189de2b083c95bc5d645d0ed53e
7
+ data.tar.gz: d7266dd23f4f0e3ca1b547add6c8ced523c9b54ec10adfc547ae46177590e6908a24784bbac9a15416ce4ba3068fb468b5c989101caa1fe49f6de754a3fff69b
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ pkg/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lasp.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Jimmy Börjesson
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,97 @@
1
+ # Läsp
2
+
3
+ A very simple Lisp implementation in Ruby. Run `lasp-repl` to play around with
4
+ it, or `lasp path/to/program.lasp` to execute a Läsp file.
5
+
6
+ ## The language
7
+
8
+ ### Examples
9
+
10
+ ```lisp
11
+ (+ 1 2 3)
12
+ ; => 6
13
+
14
+ (def x 5)
15
+ x
16
+ ; => 6
17
+
18
+ (def inc (fn (x) (+ x 1)))
19
+ (inc 5)
20
+ ; => 6
21
+ ```
22
+
23
+ ### Data types
24
+
25
+ Supports these datatypes (implemented as their Ruby counterparts)
26
+
27
+ - integer
28
+ - float
29
+ - boolean
30
+ - nil
31
+ - string
32
+
33
+ ### Comments
34
+
35
+ Comments start with a `;` and end at the end of a line
36
+
37
+ ```lisp
38
+ ; This is a comment
39
+ (+ 1 2) ; This is also a comment
40
+ ```
41
+
42
+ ### Functions in corelib
43
+
44
+ Implemented as Ruby lambdas.
45
+
46
+ - `+`
47
+ - `-`
48
+ - `*`
49
+ - `/`
50
+ - `<`
51
+ - `>`
52
+ - `=`
53
+ - `head`
54
+ - `tail`
55
+ - `cons`
56
+ - `not`
57
+ - `println`
58
+
59
+ ### Special forms
60
+
61
+ Implemented as special cases while evaluating.
62
+
63
+ - `def`
64
+ - `fn`
65
+ - `do`
66
+ - `if`
67
+
68
+ ### Functions in stdlib
69
+
70
+ Implemented in Läsp itself.
71
+
72
+ - `first` (alias of `head`)
73
+ - `rest` (alias of `tail`)
74
+ - `inc`
75
+ - `dec`
76
+ - `empty?`
77
+ - `mod`
78
+ - `complement`
79
+ - `even?`
80
+ - `odd?`
81
+ - `len`
82
+ - `nth`
83
+ - `last`
84
+ - `reverse`
85
+ - `map`
86
+ - `reduce`
87
+ - `filter`
88
+ - `sum`
89
+ - `take`
90
+ - `drop`
91
+ - `range`
92
+ - `max`
93
+ - `min`
94
+
95
+ ## Developing
96
+
97
+ Run the tests with `rspec`.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/lasp ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "./lib/eval"
4
+
5
+ # Load the stdlib
6
+ Lasp::execute_file(File.expand_path("./lib/stdlib.lasp"))
7
+
8
+ Lasp::execute_file(ARGV.first)
data/bin/lasp-repl ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "lasp"
4
+ Lasp::load_stdlib!
5
+
6
+ trap("SIGINT") { puts "\n\nBye!"; exit! }
7
+
8
+ loop do
9
+ begin
10
+ print "lasp> "
11
+ result = Lasp::execute(gets.chomp)
12
+ puts " =>> #{result.inspect}"
13
+ rescue
14
+ puts " **> #{$!}"
15
+ end
16
+ end
data/lasp.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lasp/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "lasp"
8
+ spec.version = "0.1.0"
9
+ spec.authors = ["Jimmy Börjesson"]
10
+ spec.email = ["lagginglion@gmail.com"]
11
+
12
+ spec.summary = %q{A simple programming language similar to Clojure, but much worse.}
13
+ spec.homepage = "https://github.com/alcesleo/lasp"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.10"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
@@ -0,0 +1,18 @@
1
+ module Lasp
2
+ CORELIB = {
3
+ :+ => -> (_, *args) { args.reduce(:+) },
4
+ :- => -> (_, *args) { args.reduce(:-) },
5
+ :* => -> (_, *args) { args.reduce(:*) },
6
+ :/ => -> (_, *args) { args.reduce(:/) },
7
+ :< => -> (_, *args) { args.sort == args },
8
+ :> => -> (_, *args) { args.sort.reverse == args },
9
+ :"=" => -> (_, *args) { args.uniq.count == 1 },
10
+ :list => -> (_, *args) { args },
11
+ :head => -> (_, list) { list.first },
12
+ :tail => -> (_, list) { list.drop(1) },
13
+ :cons => -> (_, item, list) { [item] + list },
14
+ :not => -> (_, arg) { !arg },
15
+ :println => -> (_, output) { puts output },
16
+ :"." => -> (_, obj, meth) { obj.send(meth) }
17
+ }
18
+ end
data/lib/lasp/env.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "lasp/corelib"
2
+
3
+ module Lasp
4
+ module_function
5
+
6
+ def global_env
7
+ @global_env ||= {}.merge(CORELIB)
8
+ end
9
+ end
data/lib/lasp/eval.rb ADDED
@@ -0,0 +1,37 @@
1
+ require "lasp/parser"
2
+ require "lasp/env"
3
+
4
+ module Lasp
5
+ module_function
6
+
7
+ def eval(ast, env)
8
+ case ast
9
+ when Symbol then env.fetch(ast)
10
+ when Array then eval_form(ast, env)
11
+ else ast
12
+ end
13
+ end
14
+
15
+ def eval_form(form, env)
16
+ head, *tail = *form
17
+
18
+ if head == :def
19
+ key, value = tail
20
+ env[key] = eval(value, env)
21
+ elsif head == :fn
22
+ params, func = tail
23
+ # Use env from context to properly scope closures
24
+ -> (_, *args) { eval(func, env.merge(Hash[params.zip(args)])) }
25
+ elsif head == :do
26
+ tail.each do |form| eval(form, env) end
27
+ elsif head == :if
28
+ conditional, true_form, false_form = tail
29
+ eval(conditional, env) ? eval(true_form, env) : eval(false_form, env)
30
+ elsif Proc === head
31
+ head.(env, *tail)
32
+ else
33
+ fn = eval(head, env)
34
+ fn.(env, *tail.map { |form| eval(form, env) })
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ module Lasp
2
+ module_function
3
+
4
+ def parse(program)
5
+ build_ast(tokenize(sanitize(program)))
6
+ end
7
+
8
+ def build_ast(tokens)
9
+ return if tokens.empty?
10
+ token = tokens.shift
11
+
12
+ if token == "("
13
+ form = []
14
+ while tokens.first != ")"
15
+ form << build_ast(tokens)
16
+ end
17
+ tokens.shift
18
+ form
19
+ else
20
+ atom(token)
21
+ end
22
+ end
23
+
24
+ def tokenize(string)
25
+ string
26
+ .gsub("(", " ( ")
27
+ .gsub(")", " ) ")
28
+ .scan(/(?:[^\s"]|"[^"]*")+/)
29
+ end
30
+
31
+ def atom(token)
32
+ case token
33
+ when "true" then true
34
+ when "false" then false
35
+ when "nil" then nil
36
+ when /\A\d+\z/ then Integer(token)
37
+ when /\A\d+.\d+\z/ then Float(token)
38
+ when /"(.*)"/ then String($1)
39
+ when /:(\w+)/ then String($1) # Symbol style strings are actually just strings
40
+ else token.to_sym
41
+ end
42
+ end
43
+
44
+ def sanitize(string)
45
+ string.gsub(/;.*$/, "")
46
+ end
47
+ end
@@ -0,0 +1,122 @@
1
+ (do
2
+ ; Aliases
3
+ (def first head)
4
+ (def rest tail)
5
+
6
+ ; Increment a number by one
7
+ (def inc (fn (x) (+ x 1)))
8
+
9
+ ; Decrement a number by one
10
+ (def dec (fn (x) (- x 1)))
11
+
12
+ ; If a list is empty
13
+ (def empty?
14
+ (fn (coll)
15
+ (= (head coll) nil)))
16
+
17
+ ; Modulus
18
+ (def mod
19
+ (fn (x y) (- x (* (/ x y) y))))
20
+
21
+ ; Returns a function that does the opposite of the given function
22
+ (def complement
23
+ (fn (f) (fn (x) (not (f x)))))
24
+
25
+ ; If a number is even
26
+ (def even?
27
+ (fn (x) (= (mod x 2) 0)))
28
+
29
+ ; If a number is odd
30
+ (def odd? (complement even?))
31
+
32
+ ; Length of a list
33
+ (def len
34
+ (fn (coll)
35
+ (if (empty? coll)
36
+ 0
37
+ (inc (len (tail coll))))))
38
+
39
+ ; Gets an item in a list by index
40
+ (def nth
41
+ (fn (index coll)
42
+ (if (= 0 index)
43
+ (head coll)
44
+ (nth (dec index) (tail coll)))))
45
+
46
+ ; Last item in list
47
+ (def last
48
+ (fn (coll)
49
+ (nth (dec (len coll)) coll)))
50
+
51
+ ; Reverses a list
52
+ (def reverse
53
+ (fn (coll)
54
+ (reduce (fn (acc item) (cons item acc)) (list) coll)))
55
+
56
+ ; Apply f to all items in list
57
+ (def map
58
+ (fn (f coll)
59
+ (if (= nil (head coll))
60
+ coll
61
+ (cons
62
+ (f (head coll))
63
+ (map f (tail coll))))))
64
+
65
+ ; Go through a list passing an accumulator and each item of the list through f
66
+ ; f(acc item)
67
+ (def reduce
68
+ (fn (f acc coll)
69
+ (if (empty? coll)
70
+ acc
71
+ (reduce f (f acc (head coll)) (tail coll)))))
72
+
73
+ ; Fil er a list of items based on a function
74
+ (def filter
75
+ (fn (f coll)
76
+ (reduce
77
+ (fn (acc item) (if (f item) (cons item acc) acc))
78
+ (list)
79
+ (reverse coll))))
80
+
81
+ ; Sum of all items in a list
82
+ (def sum
83
+ (fn (coll)
84
+ (reduce + 0 coll)))
85
+
86
+ ; Take x items from list
87
+ (def take
88
+ (fn (num coll)
89
+ (if (= num 0)
90
+ (list)
91
+ (cons (head coll) (take (dec num) (tail coll))))))
92
+
93
+ ; Drop x items from list
94
+ (def drop
95
+ (fn (num coll)
96
+ (if (= num 0)
97
+ coll
98
+ (drop (dec num) (tail coll)))))
99
+
100
+ ; Exclusive range
101
+ (def range
102
+ (fn (from to)
103
+ (if (= from to)
104
+ (list)
105
+ (cons from (range (inc from) to)))))
106
+
107
+ ; Highest value in list
108
+ (def max
109
+ (fn (coll)
110
+ (reduce
111
+ (fn (acc item) (if (< acc item) item acc))
112
+ (head coll)
113
+ (tail coll))))
114
+
115
+ ; Lowest value in list
116
+ (def min
117
+ (fn (coll)
118
+ (reduce
119
+ (fn (acc item) (if (> acc item) item acc))
120
+ (head coll)
121
+ (tail coll))))
122
+ )
@@ -0,0 +1,3 @@
1
+ module Lasp
2
+ VERSION = "0.1.0"
3
+ end
data/lib/lasp.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "lasp/version"
2
+ require "lasp/eval"
3
+
4
+ module Lasp
5
+ module_function
6
+
7
+ def execute_file(path)
8
+ execute(File.read(path))
9
+ end
10
+
11
+ def execute(program, env = global_env)
12
+ Lasp::eval(Lasp::parse(program), env)
13
+ end
14
+
15
+ def load_stdlib!
16
+ Lasp::execute_file(File.expand_path("../lasp/stdlib.lasp", __FILE__))
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lasp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jimmy Börjesson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email:
43
+ - lagginglion@gmail.com
44
+ executables:
45
+ - lasp
46
+ - lasp-repl
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gitignore"
51
+ - Gemfile
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - bin/lasp
56
+ - bin/lasp-repl
57
+ - lasp.gemspec
58
+ - lib/lasp.rb
59
+ - lib/lasp/corelib.rb
60
+ - lib/lasp/env.rb
61
+ - lib/lasp/eval.rb
62
+ - lib/lasp/parser.rb
63
+ - lib/lasp/stdlib.lasp
64
+ - lib/lasp/version.rb
65
+ homepage: https://github.com/alcesleo/lasp
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.4.5
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: A simple programming language similar to Clojure, but much worse.
89
+ test_files: []