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,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