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