purple-lang 0.0.1
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/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: []
|