nydp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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