dagon 0.1.0

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 (80) hide show
  1. data/.gitignore +2 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +86 -0
  6. data/Rakefile +40 -0
  7. data/bin/dagon +44 -0
  8. data/bin/dspec +35 -0
  9. data/bin/idg +15 -0
  10. data/bin/idgr +16 -0
  11. data/build/parser.rb +1015 -0
  12. data/build/scanner.rb +686 -0
  13. data/core/array.rb +64 -0
  14. data/core/block.rb +38 -0
  15. data/core/class.rb +91 -0
  16. data/core/false.rb +38 -0
  17. data/core/float.rb +93 -0
  18. data/core/frame.rb +24 -0
  19. data/core/hash.rb +51 -0
  20. data/core/integer.rb +98 -0
  21. data/core/object.rb +50 -0
  22. data/core/string.rb +57 -0
  23. data/core/true.rb +37 -0
  24. data/core/vm.rb +125 -0
  25. data/core/void.rb +37 -0
  26. data/dagon.gemspec +19 -0
  27. data/dagon.vim +45 -0
  28. data/dagon/ast/array_node.rb +17 -0
  29. data/dagon/ast/assignment_node.rb +21 -0
  30. data/dagon/ast/block_node.rb +19 -0
  31. data/dagon/ast/class_definition_node.rb +24 -0
  32. data/dagon/ast/constant_ref_node.rb +15 -0
  33. data/dagon/ast/frame.rb +23 -0
  34. data/dagon/ast/function_call_node.rb +33 -0
  35. data/dagon/ast/function_definition_node.rb +15 -0
  36. data/dagon/ast/function_node.rb +23 -0
  37. data/dagon/ast/hash_node.rb +16 -0
  38. data/dagon/ast/if_node.rb +20 -0
  39. data/dagon/ast/instance_init_node.rb +28 -0
  40. data/dagon/ast/instance_var_ref_node.rb +21 -0
  41. data/dagon/ast/literal_node.rb +23 -0
  42. data/dagon/ast/node.rb +22 -0
  43. data/dagon/ast/root_node.rb +19 -0
  44. data/dagon/ast/string_node.rb +16 -0
  45. data/dagon/ast/unary_function_call_node.rb +17 -0
  46. data/dagon/ast/var_ref_node.rb +19 -0
  47. data/dagon/ast/while_node.rb +17 -0
  48. data/dagon/parser.y +151 -0
  49. data/dagon/scanner.rl +143 -0
  50. data/examples/assert.dg +8 -0
  51. data/examples/assignment.dg +2 -0
  52. data/examples/block.dg +8 -0
  53. data/examples/class.dg +6 -0
  54. data/examples/conditional.dg +3 -0
  55. data/examples/conditions.dg +6 -0
  56. data/examples/equality.dg +6 -0
  57. data/examples/error.dg +1 -0
  58. data/examples/eval.dg +1 -0
  59. data/examples/fibonacci.dg +15 -0
  60. data/examples/greeter.dg +6 -0
  61. data/examples/input.dg +3 -0
  62. data/examples/instance_variables.dg +11 -0
  63. data/examples/iterate.dg +2 -0
  64. data/examples/method_call.dg +9 -0
  65. data/examples/method_definition.dg +4 -0
  66. data/examples/operators.dg +6 -0
  67. data/examples/output.dg +1 -0
  68. data/examples/require.dg +1 -0
  69. data/spec/array_spec.dg +26 -0
  70. data/spec/assertions.dg +11 -0
  71. data/spec/boolean_spec.dg +48 -0
  72. data/spec/dspec.dg +16 -0
  73. data/spec/float_spec.dg +15 -0
  74. data/spec/hash_spec.dg +6 -0
  75. data/spec/number_spec.dg +18 -0
  76. data/spec/return_spec.dg +12 -0
  77. data/spec/string_spec.dg +18 -0
  78. data/spec/void_spec.dg +9 -0
  79. data/spec/while_spec.dg +7 -0
  80. metadata +180 -0
@@ -0,0 +1,15 @@
1
+ module Dagon
2
+ module AST
3
+ class ConstantRefNode < Node
4
+ attr_reader :variable_name
5
+ def initialize filename, line_number, variable_name
6
+ super filename, line_number
7
+ @constant_name = variable_name
8
+ end
9
+
10
+ def evaluate interpreter
11
+ interpreter.current_object.dagon_const_get(@constant_name)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module Dagon
2
+ module AST
3
+ class Frame
4
+ attr_reader :frame_name
5
+ def initialize frame_name
6
+ @frame_name = frame_name
7
+ @local_variables = {}
8
+ end
9
+
10
+ def local_variable? name
11
+ @local_variables.key? name
12
+ end
13
+
14
+ def [](key)
15
+ @local_variables[key]
16
+ end
17
+
18
+ def []=(key, value)
19
+ @local_variables[key] = value
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ module Dagon
2
+ module AST
3
+ class DagonError < StandardError; end
4
+ class DagonArgumentError < DagonError; end
5
+
6
+ class FunctionCallNode < Node
7
+ def initialize filename, line_number, object, function_name, arguments, block
8
+ super filename, line_number
9
+ @function_name = function_name
10
+ @arguments = arguments
11
+ @block = block
12
+ @object = object
13
+ end
14
+
15
+ def evaluate interpreter
16
+ arguments = @arguments.map { |argument| argument.evaluate interpreter }
17
+ if @block
18
+ arguments << @block.evaluate(interpreter)
19
+ end
20
+ object = if @object
21
+ @object.evaluate interpreter
22
+ else
23
+ interpreter.frame.object
24
+ end
25
+ frame = Dagon::Core::Frame.new(object, @function_name)
26
+ interpreter.push_frame frame
27
+ result = object.dagon_send interpreter, @function_name, *arguments
28
+ interpreter.pop_frame
29
+ result
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ module Dagon
2
+ module AST
3
+ class FunctionDefinitionNode < Node
4
+ def initialize filename, line_number, function_name, function_object
5
+ super filename, line_number
6
+ @function_name = function_name
7
+ @function_object = function_object
8
+ end
9
+
10
+ def evaluate interpreter
11
+ interpreter.define_function @function_name, @function_object
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module Dagon
2
+ module AST
3
+ class Function < Node
4
+ def initialize filename, line_number, params, body
5
+ super filename, line_number
6
+ @params = params.map(&:variable_name) # params must be unwrapped
7
+ @body = body
8
+ end
9
+
10
+ def call(interpreter, object, *args)
11
+ unless args.size == @params.size
12
+ $stderr.puts "Wrong"
13
+ exit(1)
14
+ end
15
+ frame = interpreter.frame
16
+ args.each_with_index do |arg, index|
17
+ frame[@params[index]] = arg
18
+ end
19
+ execute_list interpreter, @body
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ require 'core/hash'
2
+
3
+ module Dagon
4
+ module AST
5
+ class HashNode < Node
6
+ def initialize(filename, line_number, assignments)
7
+ super(filename, line_number)
8
+ @assignments = assignments
9
+ end
10
+
11
+ def evaluate(interpreter)
12
+ interpreter.get_class("Hash").dagon_new(interpreter, @assignments)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module Dagon
2
+ module AST
3
+ class IfNode < Node
4
+ def initialize filename, line_number, condition, true_statements, false_statements
5
+ super filename, line_number
6
+ @condition = condition
7
+ @true_statements = true_statements
8
+ @false_statements = false_statements
9
+ end
10
+
11
+ def evaluate interpreter
12
+ if @condition.evaluate(interpreter) == Dtrue
13
+ execute_list interpreter, @true_statements
14
+ else
15
+ execute_list interpreter, @false_statements if @false_statements
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ module Dagon
2
+ module AST
3
+ class InstanceInitNode < Node
4
+ def initialize filename, line_number, klass_name, arguments, block
5
+ super filename, line_number
6
+ @klass_name = klass_name
7
+ @arguments = arguments
8
+ @block = block
9
+ end
10
+
11
+ def evaluate interpreter
12
+ arguments = @arguments.map { |argument| argument.evaluate interpreter }
13
+ if @block
14
+ arguments << BlockNode.new(@filename, @line_number, @block, interpreter.frame)
15
+ end
16
+
17
+ object = interpreter.frame.object
18
+ klass = object.dagon_const_get(@klass_name)
19
+ if klass.respond_to? :dagon_new
20
+ klass.dagon_new(interpreter, *arguments)
21
+ else
22
+ $stderr.puts "Cannot initialize object of type #{@klass_name}"
23
+ exit(1)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ module Dagon
2
+ module AST
3
+ class InstanceVarRefNode < Node
4
+ attr_reader :variable_name
5
+ def initialize filename, line_number, instance_variable_name
6
+ super filename, line_number
7
+ @instance_variable_name = instance_variable_name
8
+ end
9
+
10
+ def evaluate interpreter
11
+ current = interpreter.frame.object.get_instance_variable(@instance_variable_name)
12
+ if current
13
+ current
14
+ else
15
+ interpreter.frame.object.set_instance_variable(@instance_variable_name, Dvoid)
16
+ Dvoid
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ require 'core/integer'
2
+ require 'core/float'
3
+
4
+ module Dagon
5
+ module AST
6
+ class LiteralNode < Node
7
+ def initialize filename, line_number, literal
8
+ super filename, line_number
9
+ @value = literal
10
+ end
11
+
12
+ def evaluate interpreter
13
+ case @value.class.name
14
+ when "Fixnum" then interpreter.get_class("Integer").instance(@value)
15
+ when "Float" then interpreter.get_class("Float").instance(@value)
16
+ when "TrueClass" then Dtrue
17
+ when "FalseClass" then Dfalse
18
+ when "NilClass" then Dvoid
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ module Dagon
2
+ module AST
3
+ class Node
4
+ attr_reader :filename, :line_number
5
+ def initialize filename, line_number
6
+ @filename = filename
7
+ @line_number = line_number
8
+ end
9
+
10
+ def execute_list interpreter, nodes
11
+ nodes.map do |node|
12
+ node.evaluate(interpreter)
13
+ end.last
14
+ end
15
+
16
+ def dagon_error! string
17
+ raise DagonError, "Problem? #{string}"
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ require "core/vm"
2
+
3
+ module Dagon
4
+ module AST
5
+ class RootNode < Node
6
+ attr_accessor :vm
7
+ def initialize tree
8
+ super nil, nil
9
+ @tree = tree
10
+ @vm = Dagon::Core::VM.new
11
+ end
12
+
13
+ def evaluate vm = nil
14
+ actual_vm = vm || @vm
15
+ execute_list actual_vm, @tree
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ require 'core/string'
2
+
3
+ module Dagon
4
+ module AST
5
+ class StringNode < Node
6
+ def initialize filename, line_number, string
7
+ super filename, line_number
8
+ @value = string
9
+ end
10
+
11
+ def evaluate interpreter
12
+ interpreter.get_class("String").dagon_new(interpreter, @value)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module Dagon
2
+ module AST
3
+
4
+ class UnaryFunctionCallNode < Node
5
+ def initialize filename, line_number, object, function_name
6
+ super filename, line_number
7
+ @object = object
8
+ @function_name = function_name
9
+ end
10
+
11
+ def evaluate interpreter
12
+ object = @object.evaluate interpreter
13
+ object.dagon_send interpreter, "#{@function_name}@"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ module Dagon
2
+ module AST
3
+ class VarRefNode < Node
4
+ attr_reader :variable_name
5
+ def initialize filename, line_number, variable_name
6
+ super filename, line_number
7
+ @variable_name = variable_name
8
+ end
9
+
10
+ def evaluate interpreter
11
+ if interpreter.frame.local_variable? @variable_name
12
+ interpreter.frame[@variable_name]
13
+ else
14
+ interpreter.frame.object.dagon_send(interpreter, @variable_name)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Dagon
2
+ module AST
3
+ class WhileNode < Node
4
+ def initialize filename, line_number, condition, statements
5
+ super filename, line_number
6
+ @condition = condition
7
+ @statements = statements
8
+ end
9
+
10
+ def evaluate interpreter
11
+ while @condition.evaluate(interpreter) == Dtrue
12
+ execute_list interpreter, @statements
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,151 @@
1
+ class Dagon::Parser
2
+ prechigh
3
+ right EXPONENT
4
+ left '!'
5
+ left '&&' '||' '^'
6
+ left '*' '/'
7
+ left '+' '-'
8
+ left ':'
9
+ nonassoc '>' '<' '>=' '<=' '=' '!='
10
+ preclow
11
+ rule
12
+ program: { result = [] }
13
+ | statements { result = AST::RootNode.new(val[0]) }
14
+
15
+ block: INDENT statements DEDENT { result = val[1] }
16
+
17
+ statements: statements statement { result.push val[1] }
18
+ | statement { result = [val[0]] }
19
+
20
+ statement: class_definition
21
+ | method_definition
22
+ | assignment
23
+ | expression
24
+ | conditional_statement
25
+ | while_statement
26
+
27
+ while_statement: WHILE condition block { result = AST::WhileNode.new(@filename, nil, val[1], val[2]) }
28
+
29
+ conditional_statement: IF condition block else_stmt { result = AST::IfNode.new(@filename, nil, val[1], val[2], val[3]) }
30
+ else_stmt: { result = nil }
31
+ | ELSEIF condition block else_stmt{ result = [AST::IfNode.new(@filename, nil, val[1], val[2], val[3])] }
32
+ | ELSE block { result = val[1] }
33
+
34
+ class_definition: CONSTANT ':' block { result = AST::ClassDefinitionNode.new(@filename, nil, val[0].data, val[2]) }
35
+
36
+
37
+ assignment: method_name ASSIGNMENT expression { result = AST::AssignmentNode.new(@filename, nil, val[0].variable_name, val[2]) }
38
+ | '@' method_name ASSIGNMENT expression { result = AST::AssignmentNode.new(@filename, nil, "@#{val[1].variable_name}", val[3]) }
39
+
40
+ expression: expression '-' expression { result = call_on_object(val[0], '-', val[2]) }
41
+ | expression '+' expression { result = call_on_object(val[0], '+', val[2]) }
42
+ | expression '*' expression { result = call_on_object(val[0], '*', val[2]) }
43
+ | expression '/' expression { result = call_on_object(val[0], '/', val[2]) }
44
+ | expression '&&' expression { result = call_on_object(val[0], '&&', val[2]) }
45
+ | expression '||' expression { result = call_on_object(val[0], '||', val[2]) }
46
+ | expression '^' expression { result = call_on_object(val[0], '^', val[2]) }
47
+ | expression EXPONENT expression { result = call_on_object(val[0], '**', val[2]) }
48
+ | condition
49
+
50
+ unary_expression: '!' expression { result = AST::UnaryFunctionCallNode.new(@filename, nil, val[1], "!") }
51
+
52
+ condition: expression '>' expression { result = call_on_object(val[0], '>', val[2]) }
53
+ | expression '<' expression { result = call_on_object(val[0], '<', val[2]) }
54
+ | expression '<=' expression { result = call_on_object(val[0], '<=', val[2]) }
55
+ | expression '>=' expression { result = call_on_object(val[0], '>=', val[2]) }
56
+ | expression '=' expression { result = call_on_object(val[0], '=', val[2]) }
57
+ | expression '!=' expression { result = call_on_object(val[0], '!=', val[2]) }
58
+ | unary_expression
59
+ | term
60
+
61
+ hash: LBRACE hash_list RBRACE { result = AST::HashNode.new(@filename, nil, val[1]) }
62
+ hash_list: { result = [] }
63
+ | hash_member { result = val }
64
+ | hash_list COMMA hash_member { result << val }
65
+ hash_member: assignment { result = val[0] }
66
+
67
+ array: LBRACKET list RBRACKET { result = AST::ArrayNode.new(@filename, nil, val[1]) }
68
+ list: { result = [] }
69
+ | list_member { result = val }
70
+ | list COMMA list_member { result.push val[2] }
71
+ list_member: expression { result = val[0] }
72
+ | assignment { result = val[0] }
73
+
74
+
75
+ method_name: IDENTIFIER { result = AST::VarRefNode.new(@filename, nil, val[0].data) }
76
+
77
+ term: '@' IDENTIFIER { result = AST::InstanceVarRefNode.new(@filename, nil, "@#{val[1].data}") }
78
+ | IDENTIFIER { result = AST::VarRefNode.new(@filename, nil, val[0].data) }
79
+ | CONSTANT { result = AST::ConstantRefNode.new(@filename, nil, val[0].data) }
80
+ | literal
81
+ | array
82
+ | hash
83
+ | method_call
84
+ | object_call
85
+
86
+ literal: FLOAT { result = AST::LiteralNode.new(@filename, nil, val[0].data.to_f) }
87
+ | INTEGER { result = AST::LiteralNode.new(@filename, nil, val[0].data.to_i) }
88
+ | STRING { result = AST::StringNode.new(@filename, nil, val[0].data) }
89
+ | TRUE { result = AST::LiteralNode.new(@filename, nil, true) }
90
+ | FALSE { result = AST::LiteralNode.new(@filename, nil, false) }
91
+ | VOID { result = AST::LiteralNode.new(@filename, nil, nil) }
92
+
93
+
94
+ method_definition: method_name ':' block { result = AST::FunctionDefinitionNode.new(@filename, nil, val[0].variable_name, AST::Function.new(@filename, nil, [], val[2])) }
95
+ | method_name LPAREN list RPAREN ':' block { result = AST::FunctionDefinitionNode.new(@filename, nil, val[0].variable_name, AST::Function.new(@filename, nil, val[2], val[5])) }
96
+
97
+ method_call: term DOT method_name lambda { result = AST::FunctionCallNode.new(@filename, nil, val[0], val[2].variable_name, [], val[3]) }
98
+ | term DOT method_name LPAREN list RPAREN lambda { result = AST::FunctionCallNode.new(@filename, nil, val[0], val[2].variable_name, val[4], val[6]) }
99
+ | method_name LPAREN list RPAREN lambda { result = AST::FunctionCallNode.new(@filename, nil, nil, val[0].variable_name, val[2], val[4]) }
100
+ | term '[' expression RBRACKET { result = AST::FunctionCallNode.new(@filename, nil, val[0], '[]', [val[2]], nil) }
101
+
102
+ object_call: CONSTANT LPAREN list RPAREN lambda { result = AST::InstanceInitNode.new(@filename, nil, val[0].data, val[2], val[4]) }
103
+
104
+ multiline_lambda: ARROW LPAREN list RPAREN block { result = AST::BlockNode.new(@filename, nil, val[4], val[2]) }
105
+ | ARROW block { result = AST::BlockNode.new(@filename, nil, val[1], []) }
106
+
107
+ singleline_lambda: ARROW LPAREN list RPAREN LBRACE statement RBRACE { result = AST::BlockNode.new(@filename, nil, [val[5]], val[2]) }
108
+ | ARROW LBRACE statement RBRACE { result = AST::BlockNode.new(@filename, nil, [val[2]], []) }
109
+ lambda: { result = nil }
110
+ | multiline_lambda { result = val[0] }
111
+ | singleline_lambda { result = val[0] }
112
+
113
+ ---- header
114
+ %w(
115
+ node root_node function_call_node function_definition_node function_node
116
+ string_node literal_node var_ref_node if_node assignment_node while_node
117
+ class_definition_node instance_init_node block_node hash_node array_node
118
+ unary_function_call_node constant_ref_node instance_var_ref_node
119
+ ).each { |node| require_relative "../dagon/ast/#{node}" }
120
+
121
+ ---- inner
122
+
123
+ def initialize(tokens, filename, debug = false)
124
+ @yydebug = debug
125
+ @filename = filename
126
+ @tokens = tokens
127
+ @line = 0
128
+ end
129
+
130
+ def parse
131
+ do_parse
132
+ end
133
+
134
+ def self.parse(tokens, filename, debug = false)
135
+ new(tokens, filename, debug).parse
136
+ end
137
+
138
+ private
139
+ attr_accessor :tokens
140
+ def next_token
141
+ tokens.shift
142
+ end
143
+
144
+ def on_error error_token_id, error_value, value_stack
145
+ $stderr.puts "#{@filename}:#{error_value.line}: syntax error, unexpected #{error_value.data.inspect}", value_stack.inspect
146
+ exit
147
+ end
148
+
149
+ def call_on_object(object, method, *args)
150
+ AST::FunctionCallNode.new(@filename, nil, object, method, args, nil)
151
+ end