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,56 @@
1
+ module Nydp
2
+ class ExecuteConditionalInstruction
3
+ extend Helper
4
+ attr_reader :when_true, :when_false
5
+
6
+ def initialize when_true, when_false
7
+ @when_true, @when_false = when_true, when_false
8
+ end
9
+
10
+ def execute vm
11
+ arg = vm.pop_arg
12
+ truth = !Nydp.NIL.is?(arg)
13
+ vm.push_instructions (truth ? when_true : when_false), vm.peek_context
14
+ end
15
+
16
+ def inspect
17
+ "when_true:#{when_true.inspect}:when_false:#{when_false.inspect}"
18
+ end
19
+ def to_s
20
+ "#{when_true.car.to_s} #{when_false.car.to_s}"
21
+ end
22
+ end
23
+
24
+ class Cond
25
+ extend Helper
26
+ include Helper
27
+ attr_reader :condition, :conditional
28
+
29
+ def initialize cond, when_true, when_false
30
+ @condition, @conditional = cond, cons(ExecuteConditionalInstruction.new(when_true, when_false))
31
+ end
32
+
33
+ def execute vm
34
+ vm.push_instructions conditional, vm.peek_context
35
+ vm.push_instructions condition, vm.peek_context
36
+ end
37
+
38
+ def inspect
39
+ "cond:#{condition.inspect}:#{conditional.inspect}"
40
+ end
41
+ def to_s
42
+ "(cond #{condition.car.to_s} #{conditional.to_s})"
43
+ end
44
+
45
+ def self.build expressions, bindings
46
+ if expressions.is_a? Nydp::Pair
47
+ cond = cons Compiler.compile expressions.car, bindings
48
+ when_true = cons Compiler.compile expressions.cdr.car, bindings
49
+ when_false = cons Compiler.compile expressions.cdr.cdr.car, bindings
50
+ new(cond, when_true, when_false)
51
+ else
52
+ raise "can't compile Cond: #{expr.inspect}"
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ module Nydp
2
+ class ContextSymbol
3
+ attr_accessor :depth, :name
4
+
5
+ def initialize depth, name
6
+ @depth, @name = depth, name
7
+ end
8
+
9
+ def value context
10
+ context.nth(depth).at(name)
11
+ end
12
+
13
+ def assign value, context
14
+ context.nth(depth).set(name, value)
15
+ end
16
+
17
+ def inspect; to_s; end
18
+ def to_s
19
+ "[#{depth}]#{name}"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ module Nydp
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,52 @@
1
+ module Nydp
2
+ class InvokeFunctionInstruction
3
+ def initialize arg_count, source_expression
4
+ @source_expression = source_expression
5
+ @arg_count = arg_count
6
+ end
7
+
8
+ def execute vm
9
+ args = vm.pop_args @arg_count
10
+ args.car.invoke vm, args.cdr
11
+ rescue Exception => e
12
+ puts "failed to execute fn #{args.inspect}"
13
+ puts "source was #{source}"
14
+ puts "function was #{args.car.inspect}"
15
+ vm.error e
16
+ end
17
+
18
+ def inspect
19
+ "#{self.class.name}:#{source}"
20
+ end
21
+
22
+ def source
23
+ @source_expression
24
+ end
25
+
26
+ def to_s
27
+ source
28
+ end
29
+ end
30
+
31
+ class FunctionInvocation
32
+ extend Helper
33
+
34
+ def self.build expression, bindings
35
+ new cons(InvokeFunctionInstruction.new(expression.size, expression)), Compiler.compile_each(expression, bindings), expression
36
+ end
37
+
38
+ def initialize function_instruction, argument_instructions, source
39
+ @function_instruction, @argument_instructions, @source = function_instruction, argument_instructions, source
40
+ end
41
+
42
+ def execute vm
43
+ vm.push_instructions @function_instruction, vm.peek_context
44
+ vm.push_instructions @argument_instructions, vm.peek_context
45
+ end
46
+
47
+ def inspect; "#function_invocation:#{to_s}"; end
48
+ def to_s
49
+ @source.to_s
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ module Nydp
2
+ module Helper
3
+ def sym? expr, name
4
+ expr.is_a?(Nydp::Symbol) && (expr.is? name)
5
+ end
6
+
7
+ def pair? expr
8
+ expr.is_a?(Nydp::Pair)
9
+ end
10
+
11
+ def cons a, b=Nydp.NIL
12
+ Nydp::Pair.new a, b
13
+ end
14
+
15
+ def list *args
16
+ Nydp::Pair.from_list args
17
+ end
18
+
19
+ def sym name, ns
20
+ Nydp::Symbol.mk name, ns
21
+ end
22
+
23
+ def literal? expr
24
+ case expr
25
+ when String, Float, Integer, Fixnum, Nydp.NIL, Nydp::Symbol, Nydp::StringAtom, Nydp::Truth, Nydp::Nil
26
+ true
27
+ else
28
+ false
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,79 @@
1
+ require 'nydp/lexical_context'
2
+ require 'nydp/closure'
3
+
4
+ module Nydp
5
+ class PopArg
6
+ def self.execute vm
7
+ vm.pop_arg
8
+ end
9
+
10
+ def self.to_s
11
+ ""
12
+ end
13
+
14
+ def self.inspect
15
+ "#pop_arg"
16
+ end
17
+ end
18
+
19
+ class InterpretedFunction
20
+ include Helper
21
+ extend Helper
22
+
23
+ attr_accessor :arg_names, :body
24
+
25
+ def invoke vm, parent_context, arg_values
26
+ lc = LexicalContext.new parent_context
27
+ setup_context lc, arg_names, arg_values
28
+ vm.push_instructions self.body, lc
29
+ end
30
+
31
+ def setup_context context, names, values
32
+ if pair? names
33
+ context.set names.car, values.car
34
+ setup_context context, names.cdr, values.cdr
35
+ elsif Nydp.NIL.isnt? names
36
+ context.set names, values
37
+ end
38
+ end
39
+
40
+ def self.build arg_list, body, bindings
41
+ my_params = { }
42
+ index_parameters arg_list, my_params
43
+ ifn = Nydp::InterpretedFunction.new
44
+ ifn.arg_names = arg_list
45
+ ifn.body = compile_body body, cons(my_params, bindings), []
46
+ ifn
47
+ end
48
+
49
+ def self.compile_body body_forms, bindings, instructions
50
+ instructions << Nydp::Compiler.compile(body_forms.car, bindings)
51
+
52
+ rest = body_forms.cdr
53
+ if Nydp.NIL.is? rest
54
+ return Pair.from_list(instructions)
55
+ else
56
+ instructions << PopArg
57
+ compile_body rest, bindings, instructions
58
+ end
59
+ end
60
+
61
+ def self.index_parameters arg_list, hsh
62
+ if pair? arg_list
63
+ index_parameters arg_list.car, hsh
64
+ index_parameters arg_list.cdr, hsh
65
+ elsif Nydp.NIL.isnt? arg_list
66
+ hsh[arg_list] = hsh.size
67
+ end
68
+ end
69
+
70
+ def execute vm
71
+ vm.push_arg Closure.new(self, vm.peek_context)
72
+ end
73
+
74
+ def inspect; to_s; end
75
+ def to_s
76
+ "(fn #{arg_names.to_s} #{body.map { |b| b.to_s}.join(' ')})"
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,38 @@
1
+ class Nydp::LexicalContext
2
+ attr_reader :values, :parent
3
+
4
+ def initialize parent
5
+ @parent = parent
6
+ @values = { }
7
+ end
8
+
9
+ def nth n
10
+ case n
11
+ when 0
12
+ self
13
+ when -1
14
+ raise "wrong nesting level"
15
+ else
16
+ parent.nth(n - 1)
17
+ end
18
+ end
19
+
20
+ def at name
21
+ values[name]
22
+ end
23
+
24
+ def set name, value
25
+ values[name] = value
26
+ end
27
+
28
+ def to_s_with_indent str
29
+ me = @values.map { |k, v|
30
+ [str, k, "=>", v].join ' '
31
+ }.join "\n"
32
+ me + (parent ? parent.to_s_with_indent(" #{str}") : '')
33
+ end
34
+
35
+ def to_s
36
+ to_s_with_indent ''
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ module Nydp
2
+ class Literal
3
+ attr_reader :expression
4
+
5
+ def initialize expression
6
+ @expression = expression
7
+ end
8
+
9
+ def lisp_apply
10
+ expression
11
+ end
12
+
13
+ def self.build expression, bindings
14
+ new expression
15
+ end
16
+
17
+ def execute vm
18
+ vm.push_arg expression
19
+ end
20
+
21
+ def inspect; @expression.inspect; end
22
+ def to_s ; @expression.to_s ; end
23
+
24
+ def coerce _
25
+ [_, expression]
26
+ end
27
+
28
+ def > other
29
+ other < expression
30
+ end
31
+
32
+ def < other
33
+ other > expression
34
+ end
35
+
36
+ def == other
37
+ other.is_a?(Literal) && (self.expression == other.expression)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,112 @@
1
+ class Nydp::Pair
2
+ include Nydp::Helper, Enumerable
3
+ extend Nydp::Helper
4
+
5
+ attr_accessor :car, :cdr
6
+
7
+ def initialize car, cdr
8
+ @car, @cdr = car, cdr
9
+ end
10
+
11
+ def self.mk a, b
12
+ new a, b
13
+ end
14
+
15
+ def caar; car.car; end
16
+ def cadr; cdr.car; end
17
+ def cdar; car.cdr; end
18
+ def cddr; cdr.cdr; end
19
+
20
+ def self.parse_list list
21
+ if sym? list.slice(-2), "."
22
+ from_list(list[0...-2], list.slice(-1))
23
+ else
24
+ from_list list
25
+ end
26
+ end
27
+
28
+ def self.from_list list, last=Nydp.NIL, n=0
29
+ if n >= list.size
30
+ last
31
+ else
32
+ mk list[n], from_list(list, last, n+1)
33
+ end
34
+ end
35
+
36
+ def copy
37
+ cons(car, cdr.copy)
38
+ end
39
+
40
+ def + other
41
+ copy.append other
42
+ end
43
+
44
+ def == other
45
+ (other.respond_to? :car) && (self.car == other.car) && (self.cdr == other.cdr)
46
+ end
47
+
48
+ def size
49
+ 1 + (cdr.is_a?(Nydp::Pair) ? cdr.size : 0)
50
+ end
51
+
52
+ def each &block
53
+ yield car
54
+ cdr.each(&block) unless Nydp.NIL.is?(cdr)
55
+ end
56
+
57
+ def inspect
58
+ "(#{inspect_rest})"
59
+ end
60
+
61
+ def to_s
62
+ if car.is_a?(Nydp::Symbol) && car.is?(:quote)
63
+ if Nydp.NIL.is? cdr.cdr
64
+ "'#{cdr.car.to_s}"
65
+ else
66
+ "'#{cdr.to_s}"
67
+ end
68
+ else
69
+ "(#{to_s_rest})"
70
+ end
71
+ end
72
+
73
+ def to_s_rest
74
+ cdr_s = if cdr.is_a?(self.class)
75
+ cdr.to_s_rest
76
+ elsif Nydp.NIL.is? cdr
77
+ nil
78
+ else
79
+ ". #{cdr.to_s}"
80
+ end
81
+
82
+ [car.to_s, cdr_s].compact.join " "
83
+ end
84
+
85
+ def inspect_rest
86
+ cdr_s = if cdr.is_a?(self.class)
87
+ cdr.inspect_rest
88
+ elsif cdr == Nydp.NIL
89
+ nil
90
+ else
91
+ ". #{cdr.inspect}"
92
+ end
93
+
94
+ [car.inspect, cdr_s].compact.join " "
95
+ end
96
+
97
+ def append pair
98
+ if Nydp.NIL.is? self.cdr
99
+ self.cdr = pair
100
+ elsif pair? self.cdr
101
+ self.cdr.append pair
102
+ else
103
+ raise "can't append #{pair} to list #{self} : cdr is #{self.cdr.inspect}"
104
+ end
105
+ self
106
+ end
107
+
108
+ def repush instructions, _
109
+ instructions.push self
110
+ end
111
+
112
+ end
@@ -0,0 +1,123 @@
1
+ module Nydp
2
+ class Parser
3
+ attr_accessor :ns
4
+
5
+ def initialize ns
6
+ @ns = ns
7
+ end
8
+
9
+ def sym name
10
+ Nydp::Symbol.mk name.to_sym, ns
11
+ end
12
+
13
+ def read_list token_stream, termination_token
14
+ list = []
15
+ token = token_stream.next_token
16
+ while token != nil && token.first != termination_token
17
+ list << next_form(token, token_stream)
18
+ token = token_stream.next_token
19
+ end
20
+ Pair.parse_list list
21
+ end
22
+
23
+ def prefix_list prefix, list
24
+ case prefix
25
+ when "'"
26
+ Pair.from_list [sym(:quote), list]
27
+ when "`"
28
+ Pair.from_list [sym(:quasiquote), list]
29
+ when ","
30
+ Pair.from_list [sym(:unquote), list]
31
+ when ",@"
32
+ Pair.from_list [sym(:"unquote-splicing"), list]
33
+ else
34
+ list
35
+ end
36
+ end
37
+
38
+ def split_sym syms, pfx
39
+ return Pair.from_list [pfx] + syms.map { |s| parse_symbol s }
40
+ end
41
+
42
+ def parse_symbol txt
43
+ case txt
44
+ when /^'(.+)$/
45
+ Pair.from_list [sym(:quote), parse_symbol($1)]
46
+ when /^`(.+)$/
47
+ Pair.from_list [sym(:quasiquote), parse_symbol($1)]
48
+ when /^,@(.+)$/
49
+ Pair.from_list [sym(:"unquote-splicing"), parse_symbol($1)]
50
+ when /^,(.+)$/
51
+ Pair.from_list [sym(:unquote), parse_symbol($1)]
52
+ else
53
+ syms = txt.to_s.split /\./
54
+ return split_sym syms, sym(:"dot-syntax") if syms.length > 1
55
+
56
+ syms = txt.split /::/
57
+ return split_sym syms, sym(:"colon-colon-syntax") if syms.length > 1
58
+
59
+ syms = txt.split /:/
60
+ return split_sym syms, sym(:"colon-syntax") if syms.length > 1
61
+
62
+ syms = txt.split /->/
63
+ return split_sym syms, sym(:"arrow-syntax") if syms.length > 1
64
+
65
+ syms = txt.split(/=>/)
66
+ return split_sym syms, sym(:"rocket-syntax") if syms.length > 1
67
+
68
+ sym txt
69
+ end
70
+ end
71
+
72
+ def close_delimiter_for opener
73
+ case opener
74
+ when '"'
75
+ /"/
76
+ when /.*{$/
77
+ /}/
78
+ when /<<(.+)$/
79
+ Regexp.new $1
80
+ end
81
+ end
82
+
83
+ def next_form token, token_stream
84
+ return nil if token.nil?
85
+ case token.first
86
+ when :embed_suffix
87
+ Nydp.NIL
88
+ when :string_open_delim
89
+ string token_stream, token.last, close_delimiter_for(token.last)
90
+ when :left_paren
91
+ prefix_list token[1], read_list(token_stream, :right_paren)
92
+ when :symbol
93
+ parse_symbol token.last
94
+ when :comment
95
+ Pair.from_list [sym(:comment), token.last]
96
+ else
97
+ token.last
98
+ end
99
+ end
100
+
101
+ def expression token_stream
102
+ next_form token_stream.next_token, token_stream
103
+ end
104
+
105
+ def string token_stream, open_delimiter, close_delimiter
106
+ fragments = [sym(:"string-pieces")]
107
+ string_token = token_stream.next_string_fragment(open_delimiter, close_delimiter)
108
+ fragments << string_token
109
+ while !(string_token.is_a? StringFragmentCloseToken)
110
+ fragments << expression(token_stream)
111
+ string_token = token_stream.next_string_fragment('', close_delimiter)
112
+ fragments << string_token
113
+ end
114
+
115
+ if fragments.size == 2
116
+ tok = fragments[1]
117
+ return Nydp::StringAtom.new tok.string, tok
118
+ else
119
+ return Pair.from_list fragments
120
+ end
121
+ end
122
+ end
123
+ end