ql 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/LICENSE.txt +22 -0
- data/README +50 -0
- data/Rakefile +1 -0
- data/bin/q +20 -0
- data/examples/factorial.q +6 -0
- data/lib/q.rb +11 -0
- data/lib/q.treetop +334 -0
- data/lib/q/parser.rb +2795 -0
- data/lib/q/scope.rb +94 -0
- data/lib/q/syntax.rb +51 -0
- data/lib/q/version.rb +3 -0
- data/lib/q/vm.rb +72 -0
- data/ql.gemspec +26 -0
- data/spec/binomial_spec.rb +45 -0
- data/spec/spec_helper.rb +8 -0
- metadata +122 -0
data/lib/q/scope.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module Q
|
2
|
+
class Scope
|
3
|
+
def initialize parents = nil
|
4
|
+
@map = {}
|
5
|
+
@args = []
|
6
|
+
@parents = [ parents ].flatten.compact
|
7
|
+
@this = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def [] name
|
11
|
+
return @map[name] if has_own? name
|
12
|
+
|
13
|
+
@parents.each do |parent|
|
14
|
+
if parent.has? name
|
15
|
+
return parent[name]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
return nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def []= name, value
|
23
|
+
return @map[name] = value if has_own? name
|
24
|
+
|
25
|
+
@parents.each do |parent|
|
26
|
+
if parent.has? name
|
27
|
+
return parent[name] = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@map[name] = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def has? name
|
35
|
+
if has_own? name
|
36
|
+
return true
|
37
|
+
end
|
38
|
+
|
39
|
+
@parents.each do |parent|
|
40
|
+
if parent.has? name
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
return false
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_own? name
|
49
|
+
@map.has_key? name
|
50
|
+
end
|
51
|
+
|
52
|
+
def args= args
|
53
|
+
@args = [args].flatten
|
54
|
+
end
|
55
|
+
|
56
|
+
def args
|
57
|
+
@args
|
58
|
+
end
|
59
|
+
|
60
|
+
def this= th
|
61
|
+
if has_own? '@'
|
62
|
+
return @map['@'] = @this = th
|
63
|
+
end
|
64
|
+
|
65
|
+
@this = th
|
66
|
+
end
|
67
|
+
|
68
|
+
def this
|
69
|
+
if has_own? '@'
|
70
|
+
return @this = @map['@']
|
71
|
+
end
|
72
|
+
|
73
|
+
@this
|
74
|
+
end
|
75
|
+
|
76
|
+
def inspect
|
77
|
+
str = "Q::Scope --\n"
|
78
|
+
|
79
|
+
@map.each do |key, value|
|
80
|
+
str += " #{key} => #{value}\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
if not @parents.empty?
|
84
|
+
str += "\n parents:\n"
|
85
|
+
|
86
|
+
@parents.each do |parent|
|
87
|
+
str += " #{parent.inspect}\n"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
str
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/q/syntax.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'treetop'
|
2
|
+
|
3
|
+
module Q
|
4
|
+
module Syntax
|
5
|
+
|
6
|
+
class Function < Treetop::Runtime::SyntaxNode
|
7
|
+
def eval prefscope
|
8
|
+
defargs = args
|
9
|
+
|
10
|
+
prefscope['_'] = this = lambda { |scope|
|
11
|
+
argscope = Q::Scope.new
|
12
|
+
|
13
|
+
defargs.each_with_index do |arg, i|
|
14
|
+
argscope[arg] = scope.args[i]
|
15
|
+
end
|
16
|
+
|
17
|
+
fscope = Q::Scope.new [argscope, scope, prefscope]
|
18
|
+
fscope.this = this
|
19
|
+
|
20
|
+
fscope['_'] = statements.eval fscope
|
21
|
+
|
22
|
+
return fscope.this
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def args
|
27
|
+
arguments.elements.map do |argument|
|
28
|
+
argument.identifier.text_value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Conditional < Treetop::Runtime::SyntaxNode
|
34
|
+
def eval scope
|
35
|
+
if condition.eval(scope) == true
|
36
|
+
return consequence.eval(scope)
|
37
|
+
end
|
38
|
+
|
39
|
+
if has_otherwise?
|
40
|
+
return otherwise.consequence.eval(scope)
|
41
|
+
end
|
42
|
+
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def has_otherwise?
|
47
|
+
not otherwise.elements.nil?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/q/version.rb
ADDED
data/lib/q/vm.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'q/scope'
|
2
|
+
require 'q/parser'
|
3
|
+
|
4
|
+
module Q
|
5
|
+
class ParsingException < StandardError
|
6
|
+
def initialize input, failure_line, failure_index, failure_reason
|
7
|
+
@input = input
|
8
|
+
@failure_line = failure_line
|
9
|
+
@failure_index = failure_index
|
10
|
+
@failure_reason = failure_reason
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
@failure_reason
|
15
|
+
end
|
16
|
+
|
17
|
+
def reason
|
18
|
+
[
|
19
|
+
point,
|
20
|
+
@failure_reason
|
21
|
+
] * '\n'
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def line
|
26
|
+
@failure_line = @input.split('\n')[@failure_line - 1] if @failure_line.kind_of? Fixnum
|
27
|
+
|
28
|
+
@failure_line
|
29
|
+
end
|
30
|
+
|
31
|
+
def point
|
32
|
+
[
|
33
|
+
line,
|
34
|
+
" " * (@failure_index + 1) + "^"
|
35
|
+
] * '\n'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ToplevelScope < Q::Scope
|
40
|
+
def initialize
|
41
|
+
super()
|
42
|
+
|
43
|
+
self['puts'] = lambda { |scope|
|
44
|
+
puts scope.args
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class VM
|
50
|
+
def initialize
|
51
|
+
Q.load
|
52
|
+
|
53
|
+
@scope = ToplevelScope.new
|
54
|
+
@parser = QParser.new
|
55
|
+
end
|
56
|
+
|
57
|
+
def eval input
|
58
|
+
parsed = @parser.parse input
|
59
|
+
|
60
|
+
if parsed.nil?
|
61
|
+
raise ParsingException.new input, @parser.failure_line, @parser.failure_index, @parser.failure_reason
|
62
|
+
end
|
63
|
+
|
64
|
+
parsed.eval @scope
|
65
|
+
end
|
66
|
+
|
67
|
+
def reset with_scope = false
|
68
|
+
@parser = QParser.new
|
69
|
+
@scope = Q::Scope.new if with_scope
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/ql.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'q/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ql"
|
8
|
+
spec.version = Q::VERSION
|
9
|
+
spec.authors = ["Stojan Dimitrovski"]
|
10
|
+
spec.email = ["s.dimitrovski@gmail.com"]
|
11
|
+
spec.summary = %q{Q, a simple programming language.}
|
12
|
+
spec.description = %q{Q is a simple programming language implemented on top of Ruby with Treetop.}
|
13
|
+
spec.homepage = "https://github.com/hf/q"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
|
25
|
+
spec.add_dependency "treetop", "~> 1.4"
|
26
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'q/scope'
|
2
|
+
require 'q/parser'
|
3
|
+
|
4
|
+
describe "Binomial" do
|
5
|
+
before do
|
6
|
+
@scope = Q::Scope.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should be a number" do
|
10
|
+
expect(eval "3378;").to eq 3378
|
11
|
+
expect(eval "-3378;").to eq -3378
|
12
|
+
expect(eval "18.;").to eq 18.0
|
13
|
+
expect(eval "-18.;").to eq -18.0
|
14
|
+
expect(eval "18.789;").to eq 18.789
|
15
|
+
expect(eval "-18.232;").to eq -18.232
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should do addition" do
|
19
|
+
expect(eval "30 + 21;").to eq 30 + 21
|
20
|
+
expect(eval "30 + -21;").to eq 30 + (-21)
|
21
|
+
|
22
|
+
@scope['a'] = 15
|
23
|
+
@scope['b'] = 30
|
24
|
+
|
25
|
+
expect(eval "a + b;").to eq @scope['a'] + @scope['b']
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should do subtraction" do
|
29
|
+
expect(eval "30 - 15;").to eq 30 - 15
|
30
|
+
expect(eval "-10 - 15;").to eq -10 - 15
|
31
|
+
|
32
|
+
@scope['a'] = -15
|
33
|
+
@scope['b'] = 80
|
34
|
+
|
35
|
+
expect(eval "a - b;").to eq @scope['a'] - @scope['b']
|
36
|
+
end
|
37
|
+
|
38
|
+
def eval input
|
39
|
+
if not input.end_with? ";"
|
40
|
+
input = input + ";"
|
41
|
+
end
|
42
|
+
|
43
|
+
QParser.new.parse(input).eval(@scope)
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ql
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stojan Dimitrovski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: treetop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.4'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.4'
|
69
|
+
description: Q is a simple programming language implemented on top of Ruby with Treetop.
|
70
|
+
email:
|
71
|
+
- s.dimitrovski@gmail.com
|
72
|
+
executables:
|
73
|
+
- q
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- Gemfile
|
79
|
+
- Gemfile.lock
|
80
|
+
- LICENSE
|
81
|
+
- LICENSE.txt
|
82
|
+
- README
|
83
|
+
- Rakefile
|
84
|
+
- bin/q
|
85
|
+
- examples/factorial.q
|
86
|
+
- lib/q.rb
|
87
|
+
- lib/q.treetop
|
88
|
+
- lib/q/parser.rb
|
89
|
+
- lib/q/scope.rb
|
90
|
+
- lib/q/syntax.rb
|
91
|
+
- lib/q/version.rb
|
92
|
+
- lib/q/vm.rb
|
93
|
+
- ql.gemspec
|
94
|
+
- spec/binomial_spec.rb
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
homepage: https://github.com/hf/q
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 2.2.2
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: Q, a simple programming language.
|
120
|
+
test_files:
|
121
|
+
- spec/binomial_spec.rb
|
122
|
+
- spec/spec_helper.rb
|