dagon 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +86 -0
- data/Rakefile +40 -0
- data/bin/dagon +44 -0
- data/bin/dspec +35 -0
- data/bin/idg +15 -0
- data/bin/idgr +16 -0
- data/build/parser.rb +1015 -0
- data/build/scanner.rb +686 -0
- data/core/array.rb +64 -0
- data/core/block.rb +38 -0
- data/core/class.rb +91 -0
- data/core/false.rb +38 -0
- data/core/float.rb +93 -0
- data/core/frame.rb +24 -0
- data/core/hash.rb +51 -0
- data/core/integer.rb +98 -0
- data/core/object.rb +50 -0
- data/core/string.rb +57 -0
- data/core/true.rb +37 -0
- data/core/vm.rb +125 -0
- data/core/void.rb +37 -0
- data/dagon.gemspec +19 -0
- data/dagon.vim +45 -0
- data/dagon/ast/array_node.rb +17 -0
- data/dagon/ast/assignment_node.rb +21 -0
- data/dagon/ast/block_node.rb +19 -0
- data/dagon/ast/class_definition_node.rb +24 -0
- data/dagon/ast/constant_ref_node.rb +15 -0
- data/dagon/ast/frame.rb +23 -0
- data/dagon/ast/function_call_node.rb +33 -0
- data/dagon/ast/function_definition_node.rb +15 -0
- data/dagon/ast/function_node.rb +23 -0
- data/dagon/ast/hash_node.rb +16 -0
- data/dagon/ast/if_node.rb +20 -0
- data/dagon/ast/instance_init_node.rb +28 -0
- data/dagon/ast/instance_var_ref_node.rb +21 -0
- data/dagon/ast/literal_node.rb +23 -0
- data/dagon/ast/node.rb +22 -0
- data/dagon/ast/root_node.rb +19 -0
- data/dagon/ast/string_node.rb +16 -0
- data/dagon/ast/unary_function_call_node.rb +17 -0
- data/dagon/ast/var_ref_node.rb +19 -0
- data/dagon/ast/while_node.rb +17 -0
- data/dagon/parser.y +151 -0
- data/dagon/scanner.rl +143 -0
- data/examples/assert.dg +8 -0
- data/examples/assignment.dg +2 -0
- data/examples/block.dg +8 -0
- data/examples/class.dg +6 -0
- data/examples/conditional.dg +3 -0
- data/examples/conditions.dg +6 -0
- data/examples/equality.dg +6 -0
- data/examples/error.dg +1 -0
- data/examples/eval.dg +1 -0
- data/examples/fibonacci.dg +15 -0
- data/examples/greeter.dg +6 -0
- data/examples/input.dg +3 -0
- data/examples/instance_variables.dg +11 -0
- data/examples/iterate.dg +2 -0
- data/examples/method_call.dg +9 -0
- data/examples/method_definition.dg +4 -0
- data/examples/operators.dg +6 -0
- data/examples/output.dg +1 -0
- data/examples/require.dg +1 -0
- data/spec/array_spec.dg +26 -0
- data/spec/assertions.dg +11 -0
- data/spec/boolean_spec.dg +48 -0
- data/spec/dspec.dg +16 -0
- data/spec/float_spec.dg +15 -0
- data/spec/hash_spec.dg +6 -0
- data/spec/number_spec.dg +18 -0
- data/spec/return_spec.dg +12 -0
- data/spec/string_spec.dg +18 -0
- data/spec/void_spec.dg +9 -0
- data/spec/while_spec.dg +7 -0
- metadata +180 -0
data/core/object.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "core/class"
|
2
|
+
|
3
|
+
module Dagon
|
4
|
+
module Core
|
5
|
+
class DG_Object
|
6
|
+
attr_reader :klass
|
7
|
+
def initialize klass = nil
|
8
|
+
@ivars = {}
|
9
|
+
@klass = klass || DG_Class.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def dagon_define_class name, parent
|
13
|
+
name = name.to_sym
|
14
|
+
klass = DG_Class.new(name, parent)
|
15
|
+
@klass.dagon_const_set(name, klass)
|
16
|
+
klass
|
17
|
+
end
|
18
|
+
|
19
|
+
def dagon_const_set constant, value
|
20
|
+
@klass.dagon_const_set(constant, value)
|
21
|
+
end
|
22
|
+
|
23
|
+
def dagon_const_get constant
|
24
|
+
@klass.dagon_const_get(constant)
|
25
|
+
end
|
26
|
+
|
27
|
+
def dagon_send interpreter, name, *args
|
28
|
+
method = @klass.get_method(name)
|
29
|
+
if method
|
30
|
+
frame = Frame.new(self, self)
|
31
|
+
interpreter.push_frame frame
|
32
|
+
return_value = method.call(interpreter, self, *args) || Dvoid
|
33
|
+
interpreter.pop_frame
|
34
|
+
return_value
|
35
|
+
else
|
36
|
+
$stderr.puts "undefined method '#{name}' for #{self.inspect}:#{self.klass.name}"
|
37
|
+
exit(1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_instance_variable name, value
|
42
|
+
@ivars[name.to_sym] = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_instance_variable name
|
46
|
+
@ivars[name.to_sym]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/core/string.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Dagon
|
2
|
+
module Core
|
3
|
+
class DG_String < DG_Object
|
4
|
+
attr_reader :value
|
5
|
+
def initialize value, klass
|
6
|
+
@value = value
|
7
|
+
@klass = klass
|
8
|
+
end
|
9
|
+
|
10
|
+
def == other
|
11
|
+
value == other.value
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@value
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
%{"#{@value}"}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class DG_StringClass < DG_Class
|
24
|
+
def initialize
|
25
|
+
super("String", Dagon::Core::DG_Class.new)
|
26
|
+
end
|
27
|
+
|
28
|
+
def boot
|
29
|
+
add_method "init", ->(vm, ref, value) {
|
30
|
+
ref.instance_variable_set("@value", value)
|
31
|
+
}
|
32
|
+
add_method "+", ->(vm, ref, other) {
|
33
|
+
dagon_new(vm, ref.value + other.value)
|
34
|
+
}
|
35
|
+
add_method "=", ->(vm, ref, other) {
|
36
|
+
ref.value == other.value ? Dtrue : Dfalse
|
37
|
+
}
|
38
|
+
add_method "!=", ->(vm, ref, other) {
|
39
|
+
ref.value != other.value ? Dtrue : Dfalse
|
40
|
+
}
|
41
|
+
add_method 'length', ->(vm, ref) {
|
42
|
+
vm.get_class("Integer").instance(ref.value.length)
|
43
|
+
}
|
44
|
+
add_method 'to-i', ->(vm, ref) {
|
45
|
+
vm.get_class("Integer").instance(ref.value.to_i)
|
46
|
+
}
|
47
|
+
add_method 'to-f', ->(vm, ref) {
|
48
|
+
vm.get_class("Float").instance(ref.value.to_f)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def dagon_new interpreter, string = ""
|
53
|
+
DG_String.new(string, self)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/core/true.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Dagon
|
4
|
+
module Core
|
5
|
+
class True < DG_Object
|
6
|
+
include Singleton
|
7
|
+
def initialize
|
8
|
+
@value = true
|
9
|
+
@klass = DG_TrueClass.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
"true"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class DG_TrueClass < DG_Class
|
18
|
+
undef :dagon_new
|
19
|
+
def initialize
|
20
|
+
super("True", Dagon::Core::DG_Class.new)
|
21
|
+
end
|
22
|
+
|
23
|
+
def boot
|
24
|
+
add_method '!@', ->(vm, ref) {
|
25
|
+
Dfalse
|
26
|
+
}
|
27
|
+
add_method '=', ->(vm, ref, other) {
|
28
|
+
ref == other ? Dtrue : Dfalse
|
29
|
+
}
|
30
|
+
add_method '&&', ->(vm, ref, other) { other }
|
31
|
+
add_method '||', ->(vm, ref, other) { Dtrue }
|
32
|
+
add_method '^', ->(vm, ref, other) { other.dagon_send(vm, "!@") }
|
33
|
+
add_method 'to-s', ->(vm, ref) { vm.get_class("String").dagon_new(vm, "true") }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/core/vm.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
CORE = %w(object class array block false float frame integer string true void hash)
|
2
|
+
CORE.each do |klass|
|
3
|
+
require "core/#{klass}"
|
4
|
+
end
|
5
|
+
|
6
|
+
require "pry"
|
7
|
+
|
8
|
+
module Dagon
|
9
|
+
module Core
|
10
|
+
class VM
|
11
|
+
attr_reader :globals
|
12
|
+
def initialize main = nil
|
13
|
+
@load_paths = [File.expand_path(".")]
|
14
|
+
@required_files = []
|
15
|
+
@object = main || Dagon::Core::DG_Object.new
|
16
|
+
@stack = []
|
17
|
+
@stack.push Frame.new(@object, '(toplevel)')
|
18
|
+
@globals = {}
|
19
|
+
@classes = {}
|
20
|
+
boot_core
|
21
|
+
end
|
22
|
+
|
23
|
+
def boot_core
|
24
|
+
add_class("Array", DG_ArrayClass.new)
|
25
|
+
add_class("Block", DG_BlockClass.new)
|
26
|
+
add_class("False", DG_FalseClass.new)
|
27
|
+
add_class("Float", DG_FloatClass.new)
|
28
|
+
add_class("Hash", DG_HashClass.new)
|
29
|
+
add_class("Integer", DG_IntegerClass.new)
|
30
|
+
add_class("String", DG_StringClass.new)
|
31
|
+
add_class("True", DG_TrueClass.new)
|
32
|
+
add_class("Void", DG_VoidClass.new)
|
33
|
+
|
34
|
+
unless Kernel.const_defined?("Dtrue")
|
35
|
+
Kernel.const_set("Dtrue", Dagon::Core::True.instance)
|
36
|
+
end
|
37
|
+
unless Kernel.const_defined?("Dfalse")
|
38
|
+
Kernel.const_set("Dfalse", Dagon::Core::False.instance)
|
39
|
+
end
|
40
|
+
unless Kernel.const_defined?("Dvoid")
|
41
|
+
Kernel.const_set("Dvoid", Dagon::Core::Void.instance)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def current_object
|
46
|
+
@stack[0].object
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_class name, klass
|
50
|
+
current_object.dagon_const_set(name, klass)
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_class name
|
54
|
+
current_object.dagon_const_get(name)
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_load_path path
|
58
|
+
unless @load_paths.include? path
|
59
|
+
@load_paths << path
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def frame
|
64
|
+
@stack.last
|
65
|
+
end
|
66
|
+
|
67
|
+
def push_frame frame
|
68
|
+
@stack.push frame
|
69
|
+
@object = frame.object
|
70
|
+
end
|
71
|
+
|
72
|
+
def pop_frame
|
73
|
+
@stack.pop
|
74
|
+
@object = frame.object
|
75
|
+
end
|
76
|
+
|
77
|
+
def dagon_define_class name, parent
|
78
|
+
@object.dagon_define_class name, parent
|
79
|
+
end
|
80
|
+
|
81
|
+
def define_function name, block
|
82
|
+
if @object.respond_to? :add_method
|
83
|
+
@object.add_method name, block
|
84
|
+
else
|
85
|
+
@object.klass.add_method name, block
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def find_file_path filename
|
90
|
+
@load_paths.each do |path|
|
91
|
+
if File.exists? File.join(path, "#{filename}.dg")
|
92
|
+
return File.join(path, "#{filename}.dg")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def load_file filename
|
99
|
+
path = find_file_path(filename.value)
|
100
|
+
if path
|
101
|
+
@required_files << path
|
102
|
+
program = File.read(path)
|
103
|
+
tokens = Dagon::Scanner.tokenize(program, filename)
|
104
|
+
tree = Dagon::Parser.parse(tokens, filename, false)
|
105
|
+
tree.evaluate(self)
|
106
|
+
Dtrue
|
107
|
+
else
|
108
|
+
error "No such file or directory - #{filename.value}\n" +
|
109
|
+
"Searched: \n" +
|
110
|
+
@load_paths.map{ |path| " #{path}"}.join("\n")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def loaded? filename
|
115
|
+
@required_files.include? filename
|
116
|
+
end
|
117
|
+
|
118
|
+
def error message
|
119
|
+
$stderr.puts message
|
120
|
+
exit(1)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
data/core/void.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Dagon
|
4
|
+
module Core
|
5
|
+
class Void < DG_Object
|
6
|
+
include Singleton
|
7
|
+
def initialize
|
8
|
+
@klass = DG_VoidClass.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
""
|
13
|
+
end
|
14
|
+
|
15
|
+
def value # TODO: determine if there is a better way than this for checking equality
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"void"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class DG_VoidClass < DG_Class
|
25
|
+
undef :dagon_new
|
26
|
+
def initialize
|
27
|
+
super("Void", Dagon::Core::DG_Class.new)
|
28
|
+
end
|
29
|
+
|
30
|
+
def boot
|
31
|
+
add_method "=", ->(vm, ref, other) {
|
32
|
+
ref == other ? Dtrue : Dfalse
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/dagon.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "dagon"
|
5
|
+
gem.version = "0.1.0"
|
6
|
+
gem.authors = ["Caleb Thompson", "Matt Mongeau"]
|
7
|
+
gem.email = ["cjaysson@gmail.com", "halogenandtoast@gmail.com"]
|
8
|
+
gem.description = "The Dagon programming language: whitespace, enumerators, blocks, One Way"
|
9
|
+
gem.summary = "The Esoteric Order of Dagon"
|
10
|
+
gem.homepage = "https://github.com/calebthompson/dagon"
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split($/)
|
13
|
+
gem.executables = gem.files.grep(%r{^bin/(dagon|dspec|idgr)}).map{ |f| File.basename(f) }
|
14
|
+
gem.require_paths = %w{core dagon}
|
15
|
+
|
16
|
+
gem.add_development_dependency 'rake'
|
17
|
+
gem.add_development_dependency 'racc'
|
18
|
+
gem.add_development_dependency 'pry'
|
19
|
+
end
|
data/dagon.vim
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
" Vim syntax file
|
2
|
+
" Language: Dagon
|
3
|
+
" Maintainer: Caleb Thompson
|
4
|
+
" Latest Revision: 16 January 2013
|
5
|
+
|
6
|
+
au BufRead,BufNewFile *.dg set filetype=dagon
|
7
|
+
|
8
|
+
if exists("b:current_syntax")
|
9
|
+
finish
|
10
|
+
endif
|
11
|
+
|
12
|
+
syntax keyword dagonConditional if elseif else while break
|
13
|
+
syntax keyword dagonBoolean true false
|
14
|
+
syntax keyword dagonKeyword void
|
15
|
+
syntax keyword dagonFunction print puts
|
16
|
+
syntax match dagonNumber '\d'
|
17
|
+
syntax match dagonFloat '\d+\.\d+'
|
18
|
+
syntax region String start="'" end="'"
|
19
|
+
syntax region String start='"' end='"'
|
20
|
+
syntax region Array start="\[" end="\]"
|
21
|
+
syntax match dagonComment "\v#.*$"
|
22
|
+
syntax match dagonConstant "\v[A-Z][A-Za-z]*"
|
23
|
+
syntax match dagonIdentifier "\v-?[a-z][a-z0-9-]+"
|
24
|
+
syntax match dagonFunctionDefinition "\v-?[a-z][a-z0-9-]+:"
|
25
|
+
syntax region dagonFunctionDefinitionArgumentList start="\v-?[a-z][a-z0-9-]+\(" end="):"
|
26
|
+
syntax region dagonFunctionCall start="\v-?[a-z][a-z0-9-]+\(" end=")"
|
27
|
+
syntax match dagonOperator "\v \*\* "
|
28
|
+
syntax match dagonOperator "\v \* "
|
29
|
+
syntax match dagonOperator "\v / "
|
30
|
+
syntax match dagonOperator "\v \+ "
|
31
|
+
syntax match dagonOperator "\v - "
|
32
|
+
syntax match dagonAssignment ": "
|
33
|
+
|
34
|
+
highlight link dagonConditional Conditional
|
35
|
+
highlight link dagonKeyword Keyword
|
36
|
+
highlight link dagonBoolean Boolean
|
37
|
+
highlight link dagonIdentifier dagonFunction
|
38
|
+
highlight link dagonFunctionDefinition dagonFunction
|
39
|
+
highlight link dagonFunction Function
|
40
|
+
|
41
|
+
highlight link dagonComment Comment
|
42
|
+
highlight link dagonOperator Operator
|
43
|
+
highlight link dagonConstant Constant
|
44
|
+
|
45
|
+
let b:current_syntax = "dagon"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'core/array'
|
2
|
+
|
3
|
+
module Dagon
|
4
|
+
module AST
|
5
|
+
class ArrayNode < Node
|
6
|
+
def initialize filename, line_number, list
|
7
|
+
super filename, line_number
|
8
|
+
@list = list
|
9
|
+
end
|
10
|
+
|
11
|
+
def evaluate interpreter
|
12
|
+
evaluated_list = @list.map { |item| item.evaluate(interpreter) }
|
13
|
+
interpreter.get_class("Array").dagon_new(interpreter, evaluated_list)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Dagon
|
2
|
+
module AST
|
3
|
+
class AssignmentNode < Node
|
4
|
+
attr_reader :variable_name
|
5
|
+
attr_reader :variable_value
|
6
|
+
def initialize filename, line_number, variable_name, value
|
7
|
+
super filename, line_number
|
8
|
+
@variable_name = variable_name
|
9
|
+
@variable_value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate interpreter
|
13
|
+
if variable_name[0] == "@"
|
14
|
+
interpreter.frame.object.set_instance_variable(variable_name, value.evaluate(interpreter))
|
15
|
+
else
|
16
|
+
interpreter.frame[variable_name] = variable_value.evaluate(interpreter)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'core/block'
|
2
|
+
|
3
|
+
module Dagon
|
4
|
+
module AST
|
5
|
+
|
6
|
+
class BlockNode < Node
|
7
|
+
def initialize filename, line_number, statements, arguments
|
8
|
+
super filename, line_number
|
9
|
+
@statements = statements
|
10
|
+
@arguments = arguments
|
11
|
+
end
|
12
|
+
|
13
|
+
def evaluate interpreter
|
14
|
+
arguments = @arguments.map(&:variable_name)
|
15
|
+
interpreter.get_class("Block").dagon_new(interpreter, @statements, interpreter.frame, arguments)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'core/frame'
|
2
|
+
require 'core/class'
|
3
|
+
|
4
|
+
module Dagon
|
5
|
+
module AST
|
6
|
+
class ClassDefinitionNode < Node
|
7
|
+
def initialize filename, line_number, class_name, statements
|
8
|
+
super filename, line_number
|
9
|
+
@class_name = class_name
|
10
|
+
@statements = statements
|
11
|
+
end
|
12
|
+
|
13
|
+
def evaluate interpreter
|
14
|
+
klass = interpreter.dagon_define_class @class_name, Dagon::Core::DG_Class.new
|
15
|
+
frame = Dagon::Core::Frame.new(klass, klass.name)
|
16
|
+
interpreter.push_frame frame
|
17
|
+
@statements.each do |statement|
|
18
|
+
statement.evaluate interpreter
|
19
|
+
end
|
20
|
+
interpreter.pop_frame
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|