purple-lang 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +41 -0
- data/README.md +25 -0
- data/Rakefile +7 -0
- data/bin/purple +8 -0
- data/bin/ruby_sexp.rb +12 -0
- data/examples/basic.ppl +5 -0
- data/lib/purple.rb +3 -0
- data/lib/purple/infix.rb +81 -0
- data/lib/purple/parser.rb +34 -0
- data/lib/purple/purple_grammar.treetop +103 -0
- data/lib/purple/sexp_vm.rb +49 -0
- data/lib/purple/syntax_nodes.rb +68 -0
- data/purple-lang.gemspec +27 -0
- data/spec/boolean_spec.rb +10 -0
- data/spec/comment_spec.rb +11 -0
- data/spec/identifier_spec.rb +7 -0
- data/spec/infix_spec.rb +17 -0
- data/spec/integer_spec.rb +15 -0
- data/spec/nil_spec.rb +10 -0
- data/spec/sandbox_spec.rb +32 -0
- data/spec/sexp_vm_spec.rb +16 -0
- data/spec/source_layout_spec.rb +9 -0
- data/spec/spec_helper.rb +18 -0
- metadata +127 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
purple (0.0.1)
|
5
|
+
treetop
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
awesome_print (1.1.0)
|
11
|
+
coderay (1.0.8)
|
12
|
+
diff-lcs (1.1.3)
|
13
|
+
method_source (0.8.1)
|
14
|
+
polyglot (0.3.3)
|
15
|
+
pry (0.9.10)
|
16
|
+
coderay (~> 1.0.5)
|
17
|
+
method_source (~> 0.8)
|
18
|
+
slop (~> 3.3.1)
|
19
|
+
rake (0.9.2.2)
|
20
|
+
rspec (2.11.0)
|
21
|
+
rspec-core (~> 2.11.0)
|
22
|
+
rspec-expectations (~> 2.11.0)
|
23
|
+
rspec-mocks (~> 2.11.0)
|
24
|
+
rspec-core (2.11.1)
|
25
|
+
rspec-expectations (2.11.3)
|
26
|
+
diff-lcs (~> 1.1.3)
|
27
|
+
rspec-mocks (2.11.3)
|
28
|
+
slop (3.3.3)
|
29
|
+
treetop (1.4.12)
|
30
|
+
polyglot
|
31
|
+
polyglot (>= 0.3.1)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
awesome_print
|
38
|
+
pry
|
39
|
+
purple!
|
40
|
+
rake
|
41
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
purple
|
2
|
+
======
|
3
|
+
|
4
|
+
a programming language being built for fun.
|
5
|
+
|
6
|
+
## current features
|
7
|
+
- basic arithmetic
|
8
|
+
- variable assignment
|
9
|
+
|
10
|
+
## run example
|
11
|
+
```
|
12
|
+
bin/purple examples/basic.ppl
|
13
|
+
```
|
14
|
+
|
15
|
+
## install from gem
|
16
|
+
```
|
17
|
+
sudo gem install purple
|
18
|
+
purple <source-file>
|
19
|
+
```
|
20
|
+
|
21
|
+
## run the tests
|
22
|
+
```bash
|
23
|
+
bundle install && bundle exec rake spec
|
24
|
+
```
|
25
|
+
|
data/Rakefile
ADDED
data/bin/purple
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.push File.join(File.dirname(__FILE__), '../lib')
|
3
|
+
require 'purple'
|
4
|
+
|
5
|
+
# TODO: proper commandline-fu (options, help, etc.)
|
6
|
+
raise "must provide a source file name as argument" unless ARGV.length > 0
|
7
|
+
source = File.read(ARGV.first)
|
8
|
+
puts Purple::SexpVM.new.evaluate(Parser.parse(source))
|
data/bin/ruby_sexp.rb
ADDED
data/examples/basic.ppl
ADDED
data/lib/purple.rb
ADDED
data/lib/purple/infix.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
|
2
|
+
module Purple
|
3
|
+
module Infix
|
4
|
+
# apply associativity rules and order of precedence to an sexp.
|
5
|
+
#
|
6
|
+
# e.g. this input:
|
7
|
+
# [ [:int, 5], :-, [:int, 2], :-, [:int, 1] ]
|
8
|
+
# becomes:
|
9
|
+
# [ :-, [:-, [:int, 5], [:int, 2]], [:int, 1] ]
|
10
|
+
def self.process_infix(unordered)
|
11
|
+
rpn(shunting_yard unordered)
|
12
|
+
end
|
13
|
+
|
14
|
+
module PrecedenceTable
|
15
|
+
Operator = Struct.new(:precedence, :associativity)
|
16
|
+
|
17
|
+
def self.infix_operator?(operator)
|
18
|
+
!lookup(operator).nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.lookup(operator)
|
22
|
+
@operators[operator]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.op(associativity, *operators)
|
26
|
+
@precedence ||= 0
|
27
|
+
@operators ||= {}
|
28
|
+
operators.each do |operator|
|
29
|
+
@operators[operator] = Operator.new(@precedence, associativity)
|
30
|
+
end
|
31
|
+
@precedence += 1
|
32
|
+
end
|
33
|
+
|
34
|
+
# operator precedence, low to high
|
35
|
+
#op :left, '||'
|
36
|
+
#op :left, '&&'
|
37
|
+
#op :none, '==', '!='
|
38
|
+
#op :left, '<', '<=', '>', '>='
|
39
|
+
op :left, :+, :-
|
40
|
+
op :left, :*, :/
|
41
|
+
#op :right, '^'
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.rpn(input)
|
45
|
+
results = []
|
46
|
+
input.each do |object|
|
47
|
+
if PrecedenceTable.infix_operator? object
|
48
|
+
r, l = results.pop, results.pop
|
49
|
+
results << [object, l, r]
|
50
|
+
else
|
51
|
+
results << object
|
52
|
+
end
|
53
|
+
end
|
54
|
+
results.first
|
55
|
+
end
|
56
|
+
|
57
|
+
# given an infix expression, apply precedence and associativity
|
58
|
+
# result is in RPN
|
59
|
+
# http://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
60
|
+
def self.shunting_yard(input)
|
61
|
+
[].tap do |rpn|
|
62
|
+
operator_stack = []
|
63
|
+
input.each do |object|
|
64
|
+
if PrecedenceTable.infix_operator? object
|
65
|
+
op1 = object
|
66
|
+
op1_left = PrecedenceTable.lookup(op1).associativity == :left
|
67
|
+
op1_prec = PrecedenceTable.lookup(op1).precedence
|
68
|
+
rpn << operator_stack.pop while (op2 = operator_stack.last) &&
|
69
|
+
(op2_prec = PrecedenceTable.lookup(op2).precedence) &&
|
70
|
+
(op1_left ? op1_prec <= op2_prec : op1_prec < op2_prec)
|
71
|
+
operator_stack << op1
|
72
|
+
else
|
73
|
+
rpn << object
|
74
|
+
end
|
75
|
+
end
|
76
|
+
rpn << operator_stack.pop until operator_stack.empty?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'treetop'
|
2
|
+
require 'purple/syntax_nodes'
|
3
|
+
|
4
|
+
class Parser
|
5
|
+
Treetop.load File.join(File.expand_path(File.dirname __FILE__), 'purple_grammar.treetop')
|
6
|
+
@@parser = PurpleParser.new
|
7
|
+
|
8
|
+
def self.parse(code)
|
9
|
+
tree = @@parser.parse code
|
10
|
+
raise "Parse error at offset: #{@@parser.index} : #{@@parser.failure_reason}" if tree.nil?
|
11
|
+
clean! tree
|
12
|
+
tree.to_a
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.clean!(root)
|
18
|
+
return if root.elements.nil?
|
19
|
+
|
20
|
+
# TODO: this is a hack - fix it
|
21
|
+
# treat infix operation chains specially
|
22
|
+
if root.class == Purple::InfixOperationChain
|
23
|
+
root.elements.map! { |e| [e.primary, e.infix_operator] }
|
24
|
+
root.elements.flatten!
|
25
|
+
end
|
26
|
+
|
27
|
+
# remove stuff which is irrelevant to AST
|
28
|
+
root.elements.reject! { |n| n.class == Treetop::Runtime::SyntaxNode }
|
29
|
+
|
30
|
+
root.elements.each { |n| clean! n }
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
|
2
|
+
grammar Purple
|
3
|
+
|
4
|
+
# program structure
|
5
|
+
rule program
|
6
|
+
(comment / statement)+ <Program>
|
7
|
+
end
|
8
|
+
|
9
|
+
rule statement
|
10
|
+
space? ( assignment ) space? <Statement> /
|
11
|
+
expression
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# literals
|
16
|
+
rule nil
|
17
|
+
'nil' <NilLiteral>
|
18
|
+
end
|
19
|
+
|
20
|
+
rule true
|
21
|
+
'true' <TrueLiteral>
|
22
|
+
end
|
23
|
+
|
24
|
+
rule false
|
25
|
+
'false' <FalseLiteral>
|
26
|
+
end
|
27
|
+
|
28
|
+
rule integer
|
29
|
+
('+' / '-')? [0-9]+ <IntegerLiteral>
|
30
|
+
end
|
31
|
+
|
32
|
+
# assignment
|
33
|
+
rule assignment
|
34
|
+
identifier space? assignment_operator space? expression <Assignment>
|
35
|
+
end
|
36
|
+
|
37
|
+
# expressions
|
38
|
+
rule expression
|
39
|
+
space? (infix_expression / primary) <Expression>
|
40
|
+
end
|
41
|
+
|
42
|
+
rule infix_expression
|
43
|
+
infix_operation_chain primary <InfixExpression>
|
44
|
+
end
|
45
|
+
|
46
|
+
rule infix_operation_chain
|
47
|
+
(primary space? infix_operator space?)+ <InfixOperationChain>
|
48
|
+
end
|
49
|
+
|
50
|
+
rule primary
|
51
|
+
nil / true / false / identifier / integer
|
52
|
+
/
|
53
|
+
'(' expression ')' <Expression>
|
54
|
+
end
|
55
|
+
|
56
|
+
# operator sets
|
57
|
+
rule infix_operator
|
58
|
+
addition_operator / subtraction_operator / multiplication_operator / division_operator
|
59
|
+
end
|
60
|
+
|
61
|
+
# operators
|
62
|
+
rule assignment_operator
|
63
|
+
'=' <AssignmentOperator>
|
64
|
+
end
|
65
|
+
|
66
|
+
rule addition_operator
|
67
|
+
'+' <AdditionOperator>
|
68
|
+
end
|
69
|
+
|
70
|
+
rule subtraction_operator
|
71
|
+
'-' <SubtractionOperator>
|
72
|
+
end
|
73
|
+
|
74
|
+
rule multiplication_operator
|
75
|
+
'*' <MultiplicationOperator>
|
76
|
+
end
|
77
|
+
|
78
|
+
rule division_operator
|
79
|
+
'/' <DivisionOperator>
|
80
|
+
end
|
81
|
+
|
82
|
+
rule identifier
|
83
|
+
[a-zA-Z]+ <Identifier>
|
84
|
+
end
|
85
|
+
|
86
|
+
rule body
|
87
|
+
(expression / literal / space)* <Body>
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
rule space
|
92
|
+
[\s]+
|
93
|
+
end
|
94
|
+
|
95
|
+
rule comment
|
96
|
+
space? '#' [^"\n"]* ( "\n" / eof )
|
97
|
+
end
|
98
|
+
|
99
|
+
rule eof
|
100
|
+
!.
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Purple
|
4
|
+
class SexpVM
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@vars = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate(sexp)
|
11
|
+
f, *args = *sexp
|
12
|
+
#ap "x"*80
|
13
|
+
#ap f.inspect
|
14
|
+
#ap args.inspect
|
15
|
+
ret =case f
|
16
|
+
when :program
|
17
|
+
# TODO: eliminate redundant array surrounding each expr
|
18
|
+
ret = nil
|
19
|
+
args.each { |stmt| ret = evaluate stmt.first }
|
20
|
+
ret
|
21
|
+
when :assign
|
22
|
+
raise 'huh?' unless args.length == 2
|
23
|
+
ident, expr = args.first, args.last
|
24
|
+
name = ident.last
|
25
|
+
@vars[name] = evaluate expr
|
26
|
+
when :int
|
27
|
+
raise 'huh?' unless args.length == 1
|
28
|
+
args.first
|
29
|
+
when :+
|
30
|
+
raise 'huh?' unless args.length == 2
|
31
|
+
evaluate(args.first) + evaluate(args.last)
|
32
|
+
when :-
|
33
|
+
raise 'huh?' unless args.length == 2
|
34
|
+
evaluate(args.first) - evaluate(args.last)
|
35
|
+
when :*
|
36
|
+
raise 'huh?' unless args.length == 2
|
37
|
+
evaluate(args.first) * evaluate(args.last)
|
38
|
+
when :/
|
39
|
+
raise 'huh?' unless args.length == 2
|
40
|
+
evaluate(args.first) / evaluate(args.last)
|
41
|
+
when :ident
|
42
|
+
@vars[args.first]
|
43
|
+
else
|
44
|
+
raise "unknown sexp op: #{f}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'purple/infix.rb'
|
2
|
+
|
3
|
+
module Purple
|
4
|
+
|
5
|
+
# given a proc which generates ast for a node,
|
6
|
+
# create a subclass of Treetop::Runtime::SyntaxNode for it.
|
7
|
+
#
|
8
|
+
# e.g., these two are equivalent:
|
9
|
+
# IntegerLiteral = node_def -> { [:int, text_value.to_i] }
|
10
|
+
#
|
11
|
+
# class FalseLiteral < Treetop::Runtime::SyntaxNode
|
12
|
+
# def to_a
|
13
|
+
# [:int, text_value.to_i]
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
def self.node_def(to_ast)
|
17
|
+
klass = Class.new(Treetop::Runtime::SyntaxNode) do
|
18
|
+
@to_a_proc = to_ast
|
19
|
+
def to_a
|
20
|
+
self.instance_exec(&self.class.instance_variable_get(:@to_a_proc))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Program = node_def -> { [:program] + elements.map { |e| e.to_a } }
|
26
|
+
|
27
|
+
Statement = node_def -> { elements.map { |e| e.to_a } }
|
28
|
+
|
29
|
+
NilLiteral = node_def -> { [:nil] }
|
30
|
+
|
31
|
+
TrueLiteral = node_def -> { [:true] }
|
32
|
+
|
33
|
+
FalseLiteral = node_def -> { [:false] }
|
34
|
+
|
35
|
+
IntegerLiteral = node_def -> { [:int, text_value.to_i] }
|
36
|
+
|
37
|
+
Assignment = node_def -> { [:assign, elements.first.to_a, elements.last.to_a ] }
|
38
|
+
|
39
|
+
InfixExpression = node_def -> {
|
40
|
+
unordered = elements.first.elements.map { |e| e.to_a } << elements.last.to_a
|
41
|
+
Purple::Infix.process_infix unordered
|
42
|
+
}
|
43
|
+
|
44
|
+
InfixOperationChain = node_def -> { elements.map { |e| e.to_a } }
|
45
|
+
|
46
|
+
AssignmentOperator = node_def -> { }
|
47
|
+
|
48
|
+
AdditionOperator = node_def -> { :+ }
|
49
|
+
|
50
|
+
SubtractionOperator = node_def -> { :- }
|
51
|
+
|
52
|
+
MultiplicationOperator = node_def -> { :* }
|
53
|
+
|
54
|
+
DivisionOperator = node_def -> { :/ }
|
55
|
+
|
56
|
+
Identifier = node_def -> { [:ident, text_value] }
|
57
|
+
|
58
|
+
Expression = node_def -> {
|
59
|
+
# TODO: fix this shit
|
60
|
+
if elements.first.class == InfixExpression
|
61
|
+
elements.first.to_a
|
62
|
+
else
|
63
|
+
elements.map { |e| e.to_a }
|
64
|
+
end
|
65
|
+
}
|
66
|
+
|
67
|
+
end
|
68
|
+
|
data/purple-lang.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "purple-lang"
|
6
|
+
s.version = "0.0.1"
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = [ "Tim Miller" ]
|
9
|
+
s.email = [ "" ]
|
10
|
+
s.homepage = "https://github.com/echohead/purple"
|
11
|
+
s.summary = %q{programming language sandbox}
|
12
|
+
s.description = %q{}
|
13
|
+
|
14
|
+
s.required_ruby_version = ">= 1.9.3"
|
15
|
+
s.required_rubygems_version = ">= 1.3.7"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency "treetop", ">= 0"
|
23
|
+
s.add_development_dependency "rake", ">= 0"
|
24
|
+
s.add_development_dependency "rspec", ">= 0"
|
25
|
+
s.add_development_dependency "awesome_print", ">= 0"
|
26
|
+
s.add_development_dependency "pry", ">= 0"
|
27
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'comments' do
|
4
|
+
it 'should parse full-lines' do
|
5
|
+
parse("# a comment\nfoo").should == [:program, [[:ident, "foo"]]]
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should parse at end of line' do
|
9
|
+
parse("foo # <- the foo").should == [:program, [[:ident, "foo"]]]
|
10
|
+
end
|
11
|
+
end
|
data/spec/infix_spec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'infix' do
|
4
|
+
|
5
|
+
it 'does left-associativity' do
|
6
|
+
unordered = [ [:int, 5], :-, [:int, 2], :-, [:int, 1] ]
|
7
|
+
out = [ :-, [:-, [:int, 5], [:int, 2]], [:int, 1] ]
|
8
|
+
Purple::Infix.process_infix(unordered).should == out
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'does order of operations' do
|
12
|
+
unordered = [ [:int, 3], :+, [:int, 2], :*, [:int, 5] ]
|
13
|
+
out = [ :+, [:int, 3], [:*, [:int, 2], [:int, 5]] ]
|
14
|
+
Purple::Infix.process_infix(unordered).should == out
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'purple'
|
3
|
+
|
4
|
+
describe 'integer' do
|
5
|
+
|
6
|
+
it 'should parse into sexp' do
|
7
|
+
sexp('3').should == [:int, 3]
|
8
|
+
sexp('0').should == [:int, 0]
|
9
|
+
sexp('-512').should == [:int, -512]
|
10
|
+
sexp(' 1003456').should == [:int, 1003456]
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
|
data/spec/nil_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
code = <<-EOS
|
4
|
+
|
5
|
+
# an example program
|
6
|
+
|
7
|
+
x = 5 - 2 - 1
|
8
|
+
EOS
|
9
|
+
|
10
|
+
|
11
|
+
sexp = \
|
12
|
+
[:program,
|
13
|
+
[
|
14
|
+
# x = 5 - 2 - 1
|
15
|
+
[:assign,
|
16
|
+
[:ident, "x"],
|
17
|
+
[:-,
|
18
|
+
[:-,
|
19
|
+
[:int, 5],
|
20
|
+
[:int, 2]
|
21
|
+
],
|
22
|
+
[:int, 1]
|
23
|
+
]
|
24
|
+
]
|
25
|
+
]
|
26
|
+
]
|
27
|
+
|
28
|
+
describe 'sandbox' do
|
29
|
+
it 'should work' do
|
30
|
+
parse(code).should == sexp
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe Purple::SexpVM do
|
5
|
+
|
6
|
+
it 'should return the value of an assignment' do
|
7
|
+
Purple::SexpVM.new.evaluate([:assign, [:ident, 'x'], [:int, 5]]).should == 5
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should do basic arithmetic' do
|
11
|
+
evaluate("x = 1 + 1").should == 2
|
12
|
+
evaluate("x = 10 / 2 - 7 + 3 * 4 + 1\nx").should == 11
|
13
|
+
evaluate("x = (3 - 1) * 2 + (5 * (7 - 2))\nx").should == 29
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'purple'
|
2
|
+
require 'rspec'
|
3
|
+
require 'ap'
|
4
|
+
|
5
|
+
# parse a chunk of code and return its AST representation
|
6
|
+
def parse(code)
|
7
|
+
Parser.parse code
|
8
|
+
end
|
9
|
+
|
10
|
+
# return AST of a single expression, without surrounding boilerplate
|
11
|
+
def sexp(code)
|
12
|
+
parse(code).last.first
|
13
|
+
end
|
14
|
+
|
15
|
+
# evaluate a block of code and return its result
|
16
|
+
def evaluate(code)
|
17
|
+
Purple::SexpVM.new.evaluate parse(code)
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: purple-lang
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tim Miller
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: treetop
|
16
|
+
requirement: &6468740 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *6468740
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &6467760 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *6467760
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &6467220 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *6467220
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: awesome_print
|
49
|
+
requirement: &6466380 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *6466380
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: pry
|
60
|
+
requirement: &6465720 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *6465720
|
69
|
+
description: ''
|
70
|
+
email:
|
71
|
+
- ''
|
72
|
+
executables:
|
73
|
+
- purple
|
74
|
+
- ruby_sexp.rb
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- .gitignore
|
79
|
+
- Gemfile
|
80
|
+
- Gemfile.lock
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/purple
|
84
|
+
- bin/ruby_sexp.rb
|
85
|
+
- examples/basic.ppl
|
86
|
+
- lib/purple.rb
|
87
|
+
- lib/purple/infix.rb
|
88
|
+
- lib/purple/parser.rb
|
89
|
+
- lib/purple/purple_grammar.treetop
|
90
|
+
- lib/purple/sexp_vm.rb
|
91
|
+
- lib/purple/syntax_nodes.rb
|
92
|
+
- purple-lang.gemspec
|
93
|
+
- spec/boolean_spec.rb
|
94
|
+
- spec/comment_spec.rb
|
95
|
+
- spec/identifier_spec.rb
|
96
|
+
- spec/infix_spec.rb
|
97
|
+
- spec/integer_spec.rb
|
98
|
+
- spec/nil_spec.rb
|
99
|
+
- spec/sandbox_spec.rb
|
100
|
+
- spec/sexp_vm_spec.rb
|
101
|
+
- spec/source_layout_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
homepage: https://github.com/echohead/purple
|
104
|
+
licenses: []
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 1.9.3
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 1.3.7
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 1.8.11
|
124
|
+
signing_key:
|
125
|
+
specification_version: 3
|
126
|
+
summary: programming language sandbox
|
127
|
+
test_files: []
|