nydp 0.0.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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +58 -0
  7. data/Rakefile +1 -0
  8. data/bin/nydp +5 -0
  9. data/bin/nydp-tests +5 -0
  10. data/lib/lisp/boot.nydp +219 -0
  11. data/lib/lisp/test-runner.nydp +39 -0
  12. data/lib/lisp/tests/foundation-test.nydp +28 -0
  13. data/lib/nydp.rb +143 -0
  14. data/lib/nydp/assignment.rb +40 -0
  15. data/lib/nydp/builtin.rb +8 -0
  16. data/lib/nydp/builtin/apply.rb +16 -0
  17. data/lib/nydp/builtin/car.rb +5 -0
  18. data/lib/nydp/builtin/cdr.rb +5 -0
  19. data/lib/nydp/builtin/cdr_set.rb +8 -0
  20. data/lib/nydp/builtin/comment.rb +5 -0
  21. data/lib/nydp/builtin/cons.rb +16 -0
  22. data/lib/nydp/builtin/divide.rb +13 -0
  23. data/lib/nydp/builtin/error.rb +6 -0
  24. data/lib/nydp/builtin/eval.rb +14 -0
  25. data/lib/nydp/builtin/greater_than.rb +10 -0
  26. data/lib/nydp/builtin/hash.rb +30 -0
  27. data/lib/nydp/builtin/inspect.rb +5 -0
  28. data/lib/nydp/builtin/is_equal.rb +5 -0
  29. data/lib/nydp/builtin/less_than.rb +10 -0
  30. data/lib/nydp/builtin/millisecs.rb +7 -0
  31. data/lib/nydp/builtin/minus.rb +13 -0
  32. data/lib/nydp/builtin/plus.rb +28 -0
  33. data/lib/nydp/builtin/pre_compile.rb +5 -0
  34. data/lib/nydp/builtin/puts.rb +7 -0
  35. data/lib/nydp/builtin/quit.rb +5 -0
  36. data/lib/nydp/builtin/random_string.rb +11 -0
  37. data/lib/nydp/builtin/times.rb +13 -0
  38. data/lib/nydp/builtin/to_string.rb +12 -0
  39. data/lib/nydp/builtin/to_sym.rb +21 -0
  40. data/lib/nydp/builtin/vm_info.rb +13 -0
  41. data/lib/nydp/closure.rb +17 -0
  42. data/lib/nydp/compiler.rb +49 -0
  43. data/lib/nydp/cond.rb +56 -0
  44. data/lib/nydp/context_symbol.rb +22 -0
  45. data/lib/nydp/error.rb +4 -0
  46. data/lib/nydp/function_invocation.rb +52 -0
  47. data/lib/nydp/helper.rb +32 -0
  48. data/lib/nydp/interpreted_function.rb +79 -0
  49. data/lib/nydp/lexical_context.rb +38 -0
  50. data/lib/nydp/literal.rb +40 -0
  51. data/lib/nydp/pair.rb +112 -0
  52. data/lib/nydp/parser.rb +123 -0
  53. data/lib/nydp/string_atom.rb +24 -0
  54. data/lib/nydp/string_token.rb +21 -0
  55. data/lib/nydp/symbol.rb +47 -0
  56. data/lib/nydp/symbol_lookup.rb +43 -0
  57. data/lib/nydp/tokeniser.rb +80 -0
  58. data/lib/nydp/truth.rb +37 -0
  59. data/lib/nydp/version.rb +3 -0
  60. data/lib/nydp/vm.rb +76 -0
  61. data/nydp.gemspec +27 -0
  62. data/spec/boot_spec.rb +119 -0
  63. data/spec/embedded_spec.rb +106 -0
  64. data/spec/nypd_spec.rb +127 -0
  65. data/spec/pair_spec.rb +102 -0
  66. data/spec/parser_spec.rb +191 -0
  67. data/spec/spec_helper.rb +10 -0
  68. data/spec/symbol_spec.rb +32 -0
  69. metadata +176 -0
@@ -0,0 +1,24 @@
1
+ module Nydp
2
+ class StringAtom
3
+ attr_accessor :string, :token
4
+ def initialize string, token=nil
5
+ @string, @token = string, token
6
+ end
7
+
8
+ def to_s
9
+ string
10
+ end
11
+
12
+ def inspect
13
+ token ? token.rep : string.inspect
14
+ end
15
+
16
+ def == other
17
+ other.to_s == self.to_s
18
+ end
19
+
20
+ def + other
21
+ StringAtom.new "#{@string}#{other}"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module Nydp
2
+ class StringFragmentToken
3
+ attr_accessor :string, :rep
4
+ def initialize string, rep
5
+ @string, @rep = string, rep
6
+ end
7
+
8
+ def to_s
9
+ rep
10
+ end
11
+
12
+ def == other
13
+ %i{ string rep class }.inject(true) { |bool, attr|
14
+ bool && (self.send(attr) == other.send(attr))
15
+ }
16
+ end
17
+ end
18
+
19
+ class StringFragmentCloseToken < StringFragmentToken
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ class Nydp::Symbol
2
+ attr_accessor :name
3
+
4
+ def initialize name
5
+ @name = name.to_sym
6
+ end
7
+
8
+ def is? nm
9
+ self.name == nm.to_sym
10
+ end
11
+
12
+ def value context=nil
13
+ @value || Nydp.NIL
14
+ end
15
+
16
+ def self.mk name, ns
17
+ name = name.to_sym
18
+ return Nydp.NIL if name == :nil
19
+ return Nydp.T if name == :t
20
+ sym = ns[name]
21
+ unless sym
22
+ sym = new(name)
23
+ ns[name] = sym
24
+ end
25
+ sym
26
+ end
27
+
28
+ def self.find name, ns
29
+ ns[name.to_sym]
30
+ end
31
+
32
+ def inspect
33
+ "(sym #{name.to_s})"
34
+ end
35
+
36
+ def to_s
37
+ name.to_s
38
+ end
39
+
40
+ def == other
41
+ other.is_a?(Nydp::Symbol) && (self.name == other.name)
42
+ end
43
+
44
+ def assign value, context=nil
45
+ @value = value
46
+ end
47
+ end
@@ -0,0 +1,43 @@
1
+ require 'nydp/context_symbol'
2
+
3
+ module Nydp
4
+ class SymbolLookup
5
+ extend Helper
6
+
7
+ attr_reader :expression
8
+
9
+ def initialize expression
10
+ @expression = expression
11
+ end
12
+
13
+ def execute vm
14
+ vm.push_arg expression.value vm.peek_context
15
+ end
16
+
17
+ def assign value, context=nil
18
+ @expression.assign value, context
19
+ end
20
+
21
+ def self.build name, bindings
22
+ depth = 0
23
+ while Nydp.NIL.isnt? bindings
24
+ here = bindings.car
25
+ if here.key? name
26
+ return new ContextSymbol.new(depth, name)
27
+ else
28
+ depth += 1
29
+ bindings = bindings.cdr
30
+ end
31
+ end
32
+ new name
33
+ end
34
+
35
+ def to_s
36
+ "#lookup:#{expression}:"
37
+ end
38
+
39
+ def inspect
40
+ "#lookup_symbol:#{@expression.inspect}"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,80 @@
1
+ require "strscan"
2
+
3
+ module Nydp
4
+ class Tokeniser
5
+ attr_accessor :state, :finished
6
+
7
+ def initialize stream
8
+ @stream = stream.is_a?(String) ? nil : stream
9
+ @scanner = StringScanner.new(stream.is_a?(String) ? stream : "")
10
+ @state = :lisp
11
+ end
12
+
13
+ def no_more?
14
+ @scanner << @stream.readline if @scanner.eos? && @stream && !@stream.eof?
15
+ @scanner.eos?
16
+ end
17
+
18
+ def close_delimiter? scanner, delim
19
+ return (no_more? ? '' : nil) if (delim == :eof)
20
+ scanner.scan(delim)
21
+ end
22
+
23
+ def next_string_fragment open_delimiter, close_delimiter
24
+ s = @scanner
25
+ rep = "#{open_delimiter}"
26
+ string = ""
27
+ while (!no_more?)
28
+ if esc = s.scan(/\\/)
29
+ rep << esc
30
+ ch = s.getch
31
+ string << ch
32
+ rep << ch
33
+ elsif closer = close_delimiter?(s, close_delimiter)
34
+ rep << closer
35
+ return StringFragmentCloseToken.new(string, rep)
36
+ elsif embed_suffix = s.scan(/%%/)
37
+ rep << embed_suffix
38
+ return StringFragmentToken.new(string, rep)
39
+ else
40
+ ch = s.getch
41
+ string << ch
42
+ rep << ch
43
+ end
44
+ end
45
+
46
+ return StringFragmentCloseToken.new(string, rep) if close_delimiter == :eof
47
+ end
48
+
49
+ def next_token
50
+ s = @scanner
51
+ tok = nil
52
+ while !tok
53
+ if no_more?
54
+ @finished = true
55
+ return nil
56
+ elsif comment = s.scan(/;.*$/)
57
+ tok = [:comment, comment[1..-1].strip]
58
+ elsif open_str = s.scan(/"/)
59
+ tok = [:string_open_delim, open_str]
60
+ elsif embed_suffix = s.scan(/\]#/)
61
+ self.state = :external_text
62
+ tok = [:embed_suffix, embed_suffix]
63
+ elsif list_prefix = s.scan(/[^\s()]*\(/)
64
+ tok = [:left_paren, list_prefix[0...-1]]
65
+ elsif s.scan(/\)/)
66
+ tok = [:right_paren]
67
+ elsif number = s.scan(/[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?/)
68
+ tok = [:number, number.to_f]
69
+ elsif integer = s.scan(/[-+]?[0-9]+/)
70
+ tok = [:number, integer.to_i]
71
+ elsif atom = s.scan(/[^\s()]+/)
72
+ tok = [:symbol, atom]
73
+ else
74
+ s.getch
75
+ end
76
+ end
77
+ tok
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,37 @@
1
+ module Nydp
2
+ class Truth
3
+ def to_s ; 't' ; end
4
+ def inspect ; 't[nydp::Truth]' ; end
5
+ def assign *_ ; self ; end
6
+ end
7
+
8
+ class Nil
9
+ def car ; self ; end
10
+ def cdr ; self ; end
11
+ def size ; 0 ; end
12
+ def is? other ; other == self ; end
13
+ def isnt? other ; other != self ; end
14
+ def to_s ; "nil" ; end
15
+ def + other ; other ; end
16
+ def copy ; self ; end
17
+ def assign *_ ; self ; end
18
+
19
+ def inspect
20
+ "nil[Nydp::Nil]"
21
+ end
22
+
23
+ def execute vm
24
+ vm.push_arg self
25
+ end
26
+
27
+ def repush _, contexts
28
+ contexts.pop
29
+ end
30
+ end
31
+
32
+ @@nil = Nil.new
33
+ @@t = Truth.new
34
+
35
+ def self.NIL; @@nil; end
36
+ def self.T; @@t; end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Nydp
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,76 @@
1
+ module Nydp
2
+ class VM
3
+ include Helper
4
+ attr_accessor :instructions, :args, :contexts, :current_context
5
+
6
+ def initialize
7
+ @instructions = []
8
+ @args = []
9
+ @contexts = []
10
+ end
11
+
12
+ def thread expr
13
+ instructions.push expr
14
+ while instructions.length > 0
15
+ self.current_context = contexts.last
16
+ ii = instructions.pop
17
+ i = ii.car
18
+ ii.cdr.repush instructions, contexts
19
+ i.execute(self)
20
+ end
21
+ pop_arg
22
+ end
23
+
24
+ def peek_context; current_context; end
25
+ def pop_context; contexts.pop; end
26
+ def push_arg a; args.push a; end
27
+ def peek_arg; args.last; end
28
+ def pop_arg; args.pop; end
29
+
30
+ def push_instructions ii, ctx
31
+ instructions.push ii
32
+ contexts.push ctx
33
+ end
34
+
35
+ def pop_args count, tail=Nydp.NIL
36
+ case count
37
+ when 0
38
+ tail
39
+ else
40
+ pop_args(count - 1, cons(pop_arg, tail))
41
+ end
42
+ end
43
+
44
+ def error e
45
+ puts " error"
46
+ puts e
47
+ puts "================="
48
+ puts
49
+ puts "instruction stack"
50
+ puts "================="
51
+ instructions.each_with_index do |ii, ix|
52
+ puts "instructions##{ix} : #{ii} #{ii.source if ii.respond_to?(:source)}"
53
+ end
54
+ puts
55
+ puts
56
+ puts "context stack"
57
+ puts "================="
58
+ contexts.each_with_index do |ctx, ix|
59
+ puts "context##{ix} : #{ctx}"
60
+ end
61
+ puts
62
+ puts
63
+ puts "ruby backtrace"
64
+ puts "================="
65
+ (e.backtrace || []).each_with_index do |ctx, ix|
66
+ puts "#{ctx}"
67
+ end
68
+ puts
69
+ puts
70
+
71
+ instructions = []
72
+ contexts = []
73
+ args = [Nydp.NIL]
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nydp/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "nydp"
8
+ spec.version = Nydp::VERSION
9
+ spec.authors = ["Conan Dalton"]
10
+ spec.email = ["conan@conandalton.net"]
11
+ spec.description = %q{Not Your Daddy's Parentheses}
12
+ spec.summary = %q{A new lisp for a new age}
13
+ spec.homepage = "http://github.com/conanite/nydp"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_development_dependency 'rspec', '~> 2.9'
25
+ spec.add_development_dependency 'rspec_numbering_formatter'
26
+
27
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nydp do
4
+ let(:ns) { { } }
5
+ let(:vm) { Nydp::VM.new }
6
+
7
+ before {
8
+ Nydp.setup ns
9
+ boot_path = File.expand_path File.join File.expand_path(File.dirname(__FILE__)), '../lib/lisp/boot.nydp'
10
+ Nydp::StreamRunner.new(vm, ns, File.new(boot_path)).run
11
+ }
12
+
13
+ def sym name
14
+ Nydp::Symbol.mk name.to_sym, ns
15
+ end
16
+
17
+ def list *things
18
+ Nydp::Pair.from_list things.map { |thing|
19
+ case thing
20
+ when Symbol
21
+ sym(thing)
22
+ when Array
23
+ list(*thing)
24
+ else
25
+ thing
26
+ end
27
+ }
28
+ end
29
+
30
+ def run txt
31
+ Nydp::StreamRunner.new(vm, ns, txt).run
32
+ end
33
+
34
+ it "should map a function over a list of numbers" do
35
+ expect(run "(map (fn (x) (* x x)) '(1 2 3))").to eq Nydp::Pair.from_list [1, 4, 9]
36
+ end
37
+
38
+ describe "quasiquote" do
39
+ it "should quasiquote a standalone item" do
40
+ expect(run "`a").to eq sym(:a)
41
+ end
42
+
43
+ it "should quasiquote a plain list" do
44
+ expect(run "`(a b c)").to eq list :a, :b, :c
45
+ end
46
+
47
+ it "should quasiquote a plain list with a variable substitution" do
48
+ expect(run "(assign b 10) `(a ,b c)").to eq list :a, 10, :c
49
+ end
50
+
51
+ it "should quasiquote a plain list with a list-variable substitution" do
52
+ expect(run "(assign b '(1 2 3)) `(a ,@b c)").to eq list :a, 1, 2, 3, :c
53
+ end
54
+
55
+ it "should quasiquote a plain list with a list-variable substitution at the end" do
56
+ expect(run "(assign b '(1 2 3)) `(a ,b ,@b)").to eq list :a, [1,2,3], 1, 2, 3
57
+ end
58
+
59
+ it "should quasiquote a plain list with a list-variable substitution at the end" do
60
+ expect(run "(assign d '(1 2 3)) (assign g '(x y z)) `(a (b c ,d (e f ,@g)))").to eq list :a, [:b, :c, [1, 2, 3], [:e, :f, :x, :y, :z]]
61
+ end
62
+ end
63
+
64
+ describe "pairs" do
65
+ it "should break a list into pairs" do
66
+ result = run "(pairs '(1 a 2 b 3 c))"
67
+ expected = run "'((1 a) (2 b) (3 c))"
68
+ expect(result).to eq expected
69
+ end
70
+ end
71
+
72
+ describe :let do
73
+ it "should create an inner scope with a single variable" do
74
+ lisp = "(def x+3*z (x) (let y 3 (fn (z) (* (+ x y) z)))) ((x+3*z 2) 5)"
75
+ result = run lisp
76
+ expect(result).to eq 25
77
+ end
78
+ end
79
+
80
+ describe :flatten do
81
+ it "should return a flat list of things" do
82
+ lisp = "(flatten '((poo (x) (* x x)) (1 2 3)))"
83
+ result = run lisp
84
+ expect(result).to eq list :poo, :x, :"*", :x, :x, 1, 2, 3
85
+ end
86
+ end
87
+
88
+ describe :and do
89
+ it "should produce some nested conds" do
90
+ result = run "(pre-compile '(and a b c))"
91
+ expect(result).to eq list :cond, :a, [:cond, :b, :c]
92
+ end
93
+ end
94
+
95
+ describe :w_uniq do
96
+ it "should handle single-var case" do
97
+ result = run "(reset-uniq-counter) (pre-compile '(w/uniq a foo))"
98
+ expect(result).to eq list [:fn, [:a], :foo], [:uniq, [:quote, :a]]
99
+ end
100
+ end
101
+
102
+ describe :or do
103
+ it "should produce some nested conds" do
104
+ result = run "(reset-uniq-counter) (pre-compile '(or a b c))"
105
+ expect(result.to_s).to eq "((fn (ora-3) (cond ora-3 ora-3 ((fn (ora-4) (cond ora-4 ora-4 ((fn (ora-5) (cond ora-5 ora-5)) c))) b))) a)"
106
+ end
107
+ end
108
+
109
+ describe :join do
110
+ it "should join a list of strings together" do
111
+ joining = %{(joinstr "" '("foo" "bar" "bax"))}
112
+ expect(run joining).to eq "foobarbax"
113
+ end
114
+
115
+ it "should join a list of things together as a string" do
116
+ expect(run %{(joinstr " - " '(1 2 3))}).to eq "1 - 2 - 3"
117
+ end
118
+ end
119
+ end