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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +58 -0
- data/Rakefile +1 -0
- data/bin/nydp +5 -0
- data/bin/nydp-tests +5 -0
- data/lib/lisp/boot.nydp +219 -0
- data/lib/lisp/test-runner.nydp +39 -0
- data/lib/lisp/tests/foundation-test.nydp +28 -0
- data/lib/nydp.rb +143 -0
- data/lib/nydp/assignment.rb +40 -0
- data/lib/nydp/builtin.rb +8 -0
- data/lib/nydp/builtin/apply.rb +16 -0
- data/lib/nydp/builtin/car.rb +5 -0
- data/lib/nydp/builtin/cdr.rb +5 -0
- data/lib/nydp/builtin/cdr_set.rb +8 -0
- data/lib/nydp/builtin/comment.rb +5 -0
- data/lib/nydp/builtin/cons.rb +16 -0
- data/lib/nydp/builtin/divide.rb +13 -0
- data/lib/nydp/builtin/error.rb +6 -0
- data/lib/nydp/builtin/eval.rb +14 -0
- data/lib/nydp/builtin/greater_than.rb +10 -0
- data/lib/nydp/builtin/hash.rb +30 -0
- data/lib/nydp/builtin/inspect.rb +5 -0
- data/lib/nydp/builtin/is_equal.rb +5 -0
- data/lib/nydp/builtin/less_than.rb +10 -0
- data/lib/nydp/builtin/millisecs.rb +7 -0
- data/lib/nydp/builtin/minus.rb +13 -0
- data/lib/nydp/builtin/plus.rb +28 -0
- data/lib/nydp/builtin/pre_compile.rb +5 -0
- data/lib/nydp/builtin/puts.rb +7 -0
- data/lib/nydp/builtin/quit.rb +5 -0
- data/lib/nydp/builtin/random_string.rb +11 -0
- data/lib/nydp/builtin/times.rb +13 -0
- data/lib/nydp/builtin/to_string.rb +12 -0
- data/lib/nydp/builtin/to_sym.rb +21 -0
- data/lib/nydp/builtin/vm_info.rb +13 -0
- data/lib/nydp/closure.rb +17 -0
- data/lib/nydp/compiler.rb +49 -0
- data/lib/nydp/cond.rb +56 -0
- data/lib/nydp/context_symbol.rb +22 -0
- data/lib/nydp/error.rb +4 -0
- data/lib/nydp/function_invocation.rb +52 -0
- data/lib/nydp/helper.rb +32 -0
- data/lib/nydp/interpreted_function.rb +79 -0
- data/lib/nydp/lexical_context.rb +38 -0
- data/lib/nydp/literal.rb +40 -0
- data/lib/nydp/pair.rb +112 -0
- data/lib/nydp/parser.rb +123 -0
- data/lib/nydp/string_atom.rb +24 -0
- data/lib/nydp/string_token.rb +21 -0
- data/lib/nydp/symbol.rb +47 -0
- data/lib/nydp/symbol_lookup.rb +43 -0
- data/lib/nydp/tokeniser.rb +80 -0
- data/lib/nydp/truth.rb +37 -0
- data/lib/nydp/version.rb +3 -0
- data/lib/nydp/vm.rb +76 -0
- data/nydp.gemspec +27 -0
- data/spec/boot_spec.rb +119 -0
- data/spec/embedded_spec.rb +106 -0
- data/spec/nypd_spec.rb +127 -0
- data/spec/pair_spec.rb +102 -0
- data/spec/parser_spec.rb +191 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/symbol_spec.rb +32 -0
- 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
|
data/lib/nydp/symbol.rb
ADDED
@@ -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
|
data/lib/nydp/truth.rb
ADDED
@@ -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
|
data/lib/nydp/version.rb
ADDED
data/lib/nydp/vm.rb
ADDED
@@ -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
|
data/nydp.gemspec
ADDED
@@ -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
|
data/spec/boot_spec.rb
ADDED
@@ -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
|