dagon 0.1.0

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