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