crisp 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +35 -0
- data/LICENSE +21 -0
- data/README.md +32 -0
- data/Rakefile +44 -0
- data/bin/crisp +13 -0
- data/crisp.gemspec +72 -0
- data/lib/crisp.rb +17 -0
- data/lib/crisp/chained_env.rb +20 -0
- data/lib/crisp/crisp.treetop +67 -0
- data/lib/crisp/env.rb +21 -0
- data/lib/crisp/errors.rb +12 -0
- data/lib/crisp/function.rb +59 -0
- data/lib/crisp/functions.rb +11 -0
- data/lib/crisp/functions/arithmetic.rb +25 -0
- data/lib/crisp/functions/core.rb +53 -0
- data/lib/crisp/nodes.rb +62 -0
- data/lib/crisp/parser.rb +17 -0
- data/lib/crisp/runtime.rb +12 -0
- data/lib/crisp/shell.rb +21 -0
- data/spec/crisp/arithmetics_spec.rb +67 -0
- data/spec/crisp/basic_spec.rb +92 -0
- data/spec/crisp/string_spec.rb +9 -0
- data/spec/spec_helper.rb +21 -0
- metadata +124 -0
data/CHANGELOG.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
## 0.0.6 (2010-xx-xx)
|
2
|
+
|
3
|
+
* todo
|
4
|
+
|
5
|
+
## 0.0.5 (2010-11-29)
|
6
|
+
|
7
|
+
* improved interactive shell (readline support)
|
8
|
+
* internal improvments
|
9
|
+
|
10
|
+
## 0.0.4 (2010-11-27)
|
11
|
+
|
12
|
+
* functions :-)
|
13
|
+
* basic array support
|
14
|
+
* more infos on syntax failures
|
15
|
+
* grammar will be loaded dynamically
|
16
|
+
* improved commandline tool
|
17
|
+
* internal/structural improvements
|
18
|
+
|
19
|
+
## 0.0.3 (2010-10-30)
|
20
|
+
|
21
|
+
* added support for float and negative numbers
|
22
|
+
* internal cleanup
|
23
|
+
* basic symbol support
|
24
|
+
* basic string support
|
25
|
+
* support of multiple statements
|
26
|
+
|
27
|
+
## 0.0.2 (2010-07-25)
|
28
|
+
|
29
|
+
* more specs
|
30
|
+
* multiply and divide support
|
31
|
+
* refactored functions handling
|
32
|
+
|
33
|
+
## 0.0.1 (2010-07-24)
|
34
|
+
|
35
|
+
* first basic arithmetic features
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2010 Markus Gerdes
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
## Crisp
|
2
|
+
|
3
|
+
Crisp is an experimental language written in Ruby, using treetop.
|
4
|
+
|
5
|
+
Crisp has a Lisp syntax and immutable data structures.
|
6
|
+
|
7
|
+
The main purpose of the language is to deal with the issues and problems when creating your own programming language.
|
8
|
+
|
9
|
+
### Example
|
10
|
+
|
11
|
+
>> (* 2 3)
|
12
|
+
=> 6
|
13
|
+
>> (def foo 4)
|
14
|
+
=> 4
|
15
|
+
>> (/ (* foo foo) 2 2)
|
16
|
+
=> 4
|
17
|
+
>> (def add2 (fn [arg] (+ 2 arg)))
|
18
|
+
=> #<Crisp::Function:0x85d1bc0>
|
19
|
+
>> (add2 5)
|
20
|
+
=> 7
|
21
|
+
|
22
|
+
### Installation
|
23
|
+
|
24
|
+
gem is coming soon.
|
25
|
+
|
26
|
+
### Usage
|
27
|
+
|
28
|
+
To start an interactive shell:
|
29
|
+
crisp
|
30
|
+
|
31
|
+
To run a crisp programm
|
32
|
+
crisp /path/to/file
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
$:.unshift('./lib')
|
8
|
+
require 'lib/crisp'
|
9
|
+
|
10
|
+
Jeweler::Tasks.new do |gem|
|
11
|
+
gem.name = "crisp"
|
12
|
+
gem.version = Crisp::VERSION
|
13
|
+
gem.summary = 'a tiny lisp-like language written in ruby using treetop.'
|
14
|
+
gem.email = "github@mgsnova.de"
|
15
|
+
gem.homepage = "http://github.com/mgsnova/crisp"
|
16
|
+
gem.authors = ['Markus Gerdes']
|
17
|
+
gem.add_dependency 'treetop', '~> 1.4.0'
|
18
|
+
gem.add_development_dependency 'rspec', '~> 2.2.0'
|
19
|
+
end
|
20
|
+
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
def gemspec
|
27
|
+
@gemspec ||= begin
|
28
|
+
file = File.expand_path('../crisp.gemspec', __FILE__)
|
29
|
+
eval(File.read(file), binding, file)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Rake::GemPackageTask.new(gemspec) do |pkg|
|
34
|
+
pkg.need_zip = true
|
35
|
+
pkg.need_tar = true
|
36
|
+
end
|
37
|
+
|
38
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
39
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
40
|
+
spec.rspec_opts = ['--colour', '-f documentation', '--backtrace']
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => [:gemspec, :spec, :gem] do
|
44
|
+
end
|
data/bin/crisp
ADDED
data/crisp.gemspec
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{crisp}
|
8
|
+
s.version = "0.0.5"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Markus Gerdes"]
|
12
|
+
s.date = %q{2010-11-29}
|
13
|
+
s.default_executable = %q{crisp}
|
14
|
+
s.email = %q{github@mgsnova.de}
|
15
|
+
s.executables = ["crisp"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE",
|
18
|
+
"README.md"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
"CHANGELOG.md",
|
22
|
+
"LICENSE",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"bin/crisp",
|
26
|
+
"crisp.gemspec",
|
27
|
+
"lib/crisp.rb",
|
28
|
+
"lib/crisp/chained_env.rb",
|
29
|
+
"lib/crisp/crisp.treetop",
|
30
|
+
"lib/crisp/env.rb",
|
31
|
+
"lib/crisp/errors.rb",
|
32
|
+
"lib/crisp/function.rb",
|
33
|
+
"lib/crisp/functions.rb",
|
34
|
+
"lib/crisp/functions/arithmetic.rb",
|
35
|
+
"lib/crisp/functions/core.rb",
|
36
|
+
"lib/crisp/nodes.rb",
|
37
|
+
"lib/crisp/parser.rb",
|
38
|
+
"lib/crisp/runtime.rb",
|
39
|
+
"lib/crisp/shell.rb",
|
40
|
+
"spec/crisp/arithmetics_spec.rb",
|
41
|
+
"spec/crisp/basic_spec.rb",
|
42
|
+
"spec/crisp/string_spec.rb",
|
43
|
+
"spec/spec_helper.rb"
|
44
|
+
]
|
45
|
+
s.homepage = %q{http://github.com/mgsnova/crisp}
|
46
|
+
s.require_paths = ["lib"]
|
47
|
+
s.rubygems_version = %q{1.3.7}
|
48
|
+
s.summary = %q{a tiny lisp-like language written in ruby using treetop.}
|
49
|
+
s.test_files = [
|
50
|
+
"spec/crisp/arithmetics_spec.rb",
|
51
|
+
"spec/crisp/basic_spec.rb",
|
52
|
+
"spec/crisp/string_spec.rb",
|
53
|
+
"spec/spec_helper.rb"
|
54
|
+
]
|
55
|
+
|
56
|
+
if s.respond_to? :specification_version then
|
57
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
58
|
+
s.specification_version = 3
|
59
|
+
|
60
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
61
|
+
s.add_runtime_dependency(%q<treetop>, ["~> 1.4.0"])
|
62
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.2.0"])
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<treetop>, ["~> 1.4.0"])
|
65
|
+
s.add_dependency(%q<rspec>, ["~> 2.2.0"])
|
66
|
+
end
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<treetop>, ["~> 1.4.0"])
|
69
|
+
s.add_dependency(%q<rspec>, ["~> 2.2.0"])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
data/lib/crisp.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "treetop"
|
3
|
+
require "pp"
|
4
|
+
|
5
|
+
module Crisp
|
6
|
+
VERSION = '0.0.5'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'crisp/errors'
|
10
|
+
require 'crisp/parser'
|
11
|
+
require 'crisp/runtime'
|
12
|
+
require 'crisp/chained_env'
|
13
|
+
require 'crisp/env'
|
14
|
+
require 'crisp/nodes'
|
15
|
+
require 'crisp/function'
|
16
|
+
require 'crisp/functions'
|
17
|
+
require 'crisp/shell'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Crisp
|
2
|
+
class ChainedEnv
|
3
|
+
def initialize(first, second)
|
4
|
+
@first = first
|
5
|
+
@second = second
|
6
|
+
end
|
7
|
+
|
8
|
+
def [](key)
|
9
|
+
if @first.has_key?(key)
|
10
|
+
@first[key]
|
11
|
+
else
|
12
|
+
@second[key]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(key, val)
|
17
|
+
@second[key] = val
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
grammar Crisp
|
2
|
+
|
3
|
+
rule block
|
4
|
+
(operation)* <Block>
|
5
|
+
end
|
6
|
+
|
7
|
+
rule operation
|
8
|
+
space paren_start space func_identifier element_list:(space element)* space paren_end space <Operation>
|
9
|
+
end
|
10
|
+
|
11
|
+
rule array
|
12
|
+
array_paren_start element_list:(space element)* space array_paren_end <ArrayLiteral>
|
13
|
+
end
|
14
|
+
|
15
|
+
rule element
|
16
|
+
operation / float / number / symbol / string / array
|
17
|
+
end
|
18
|
+
|
19
|
+
rule func_identifier
|
20
|
+
symbol
|
21
|
+
/
|
22
|
+
'+'
|
23
|
+
/
|
24
|
+
'-'
|
25
|
+
/
|
26
|
+
'*'
|
27
|
+
/
|
28
|
+
'/'
|
29
|
+
end
|
30
|
+
|
31
|
+
rule symbol
|
32
|
+
[a-z] [a-z0-9]* <Symbol>
|
33
|
+
end
|
34
|
+
|
35
|
+
rule number
|
36
|
+
'-'? ([1-9] [0-9]* / '0') <Number>
|
37
|
+
end
|
38
|
+
|
39
|
+
rule float
|
40
|
+
number '.' [0-9]* <Float>
|
41
|
+
end
|
42
|
+
|
43
|
+
rule string
|
44
|
+
'"' [^"]* '"' <StringLiteral>
|
45
|
+
end
|
46
|
+
|
47
|
+
rule space
|
48
|
+
[ \t\r\n]*
|
49
|
+
end
|
50
|
+
|
51
|
+
rule paren_start
|
52
|
+
"("
|
53
|
+
end
|
54
|
+
|
55
|
+
rule paren_end
|
56
|
+
")"
|
57
|
+
end
|
58
|
+
|
59
|
+
rule array_paren_start
|
60
|
+
"["
|
61
|
+
end
|
62
|
+
|
63
|
+
rule array_paren_end
|
64
|
+
"]"
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/crisp/env.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Crisp
|
2
|
+
class Env
|
3
|
+
def initialize
|
4
|
+
@map = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def has_key?(key)
|
8
|
+
@map.has_key?(key.to_sym)
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
@map[key.to_sym]
|
13
|
+
end
|
14
|
+
|
15
|
+
def []=(key, val)
|
16
|
+
key = key.to_sym
|
17
|
+
raise EnvironmentError, "#{key} already binded" if @map.has_key?(key)
|
18
|
+
@map[key] = val
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/crisp/errors.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Crisp
|
2
|
+
class Function
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(&blk)
|
6
|
+
@name = nil
|
7
|
+
@blk = blk
|
8
|
+
end
|
9
|
+
|
10
|
+
def bind(name, env)
|
11
|
+
@name = name.to_sym
|
12
|
+
env[name] = self
|
13
|
+
end
|
14
|
+
|
15
|
+
def eval(env, params = [])
|
16
|
+
@env = env
|
17
|
+
@params = params
|
18
|
+
self.instance_eval(&@blk)
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def validate_params_count(expected, got)
|
24
|
+
if expected != got
|
25
|
+
raise ArgumentError, "wrong number of arguments for '#{name}' (#{got} for #{expected})"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def params_values
|
30
|
+
params.map do |param|
|
31
|
+
param.eval(env)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def params_evaled
|
36
|
+
params_values.map do |param|
|
37
|
+
if param.class.to_s == 'Symbol'
|
38
|
+
if env[param].respond_to?(:eval)
|
39
|
+
env[param].eval(env)
|
40
|
+
else
|
41
|
+
env[param]
|
42
|
+
end
|
43
|
+
else
|
44
|
+
param
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def env
|
52
|
+
@env
|
53
|
+
end
|
54
|
+
|
55
|
+
def params
|
56
|
+
@params
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Crisp
|
2
|
+
module Functions
|
3
|
+
class Arithmetic
|
4
|
+
def self.load(env)
|
5
|
+
|
6
|
+
Function.new do
|
7
|
+
params_evaled.inject(:+)
|
8
|
+
end.bind('+', env)
|
9
|
+
|
10
|
+
Function.new do
|
11
|
+
params_evaled.inject(:-)
|
12
|
+
end.bind('-', env)
|
13
|
+
|
14
|
+
Function.new do
|
15
|
+
params_evaled.inject(:*)
|
16
|
+
end.bind('*', env)
|
17
|
+
|
18
|
+
Function.new do
|
19
|
+
params_evaled.inject(:/)
|
20
|
+
end.bind('/', env)
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Crisp
|
2
|
+
module Functions
|
3
|
+
class Core
|
4
|
+
def self.load(env)
|
5
|
+
|
6
|
+
Function.new do
|
7
|
+
print params_evaled.collect(&:to_s).join(' ') + "\n"
|
8
|
+
end.bind('println', env)
|
9
|
+
|
10
|
+
Function.new do
|
11
|
+
validate_params_count(2, params.size)
|
12
|
+
|
13
|
+
value = params_evaled[1]
|
14
|
+
|
15
|
+
if value.class.name == "Crisp::Function"
|
16
|
+
value.bind(params_values[0], env)
|
17
|
+
else
|
18
|
+
env[params_values[0]] = value
|
19
|
+
end
|
20
|
+
end.bind('def', env)
|
21
|
+
|
22
|
+
Function.new do
|
23
|
+
validate_params_count(2, params.size)
|
24
|
+
|
25
|
+
if params[0].class.name != "Crisp::ArrayLiteral"
|
26
|
+
raise ArgumentError, "no parameter list defined"
|
27
|
+
end
|
28
|
+
|
29
|
+
if params[1].class.name != "Crisp::Operation"
|
30
|
+
raise ArgumentError, "no function body defined"
|
31
|
+
end
|
32
|
+
|
33
|
+
fn_param_list = params[0].raw_elements
|
34
|
+
fn_operation = params[1]
|
35
|
+
|
36
|
+
Function.new do
|
37
|
+
validate_params_count(fn_param_list.size, params.size)
|
38
|
+
|
39
|
+
local_env = Env.new
|
40
|
+
fn_param_list.each_with_index do |key, idx|
|
41
|
+
local_env[key.eval(env)] = params[idx]
|
42
|
+
end
|
43
|
+
|
44
|
+
chained_env = ChainedEnv.new(local_env, env)
|
45
|
+
|
46
|
+
fn_operation.eval(chained_env)
|
47
|
+
end
|
48
|
+
end.bind('fn', env)
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/crisp/nodes.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Crisp
|
2
|
+
class Base < Treetop::Runtime::SyntaxNode
|
3
|
+
def eval(env)
|
4
|
+
nil
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Operation < Base
|
9
|
+
def eval(env)
|
10
|
+
env[self.func_identifier.text_value].eval(env, self.element_list.elements.collect(&:element))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Block < Base
|
15
|
+
def eval(env)
|
16
|
+
last_result = nil
|
17
|
+
|
18
|
+
elements.each do |op|
|
19
|
+
last_result = op.eval(env)
|
20
|
+
end
|
21
|
+
|
22
|
+
last_result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ArrayLiteral < Base
|
27
|
+
def eval(env)
|
28
|
+
raw_elements.map { |e| e.eval(env) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def raw_elements
|
32
|
+
self.element_list.elements.collect(&:element)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Primitive < Base
|
37
|
+
end
|
38
|
+
|
39
|
+
class Number < Primitive
|
40
|
+
def eval(env)
|
41
|
+
text_value.to_i
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Float < Primitive
|
46
|
+
def eval(env)
|
47
|
+
text_value.to_f
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class StringLiteral < Primitive
|
52
|
+
def eval(env)
|
53
|
+
text_value[1..-2]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Symbol < Primitive
|
58
|
+
def eval(env)
|
59
|
+
text_value.to_sym
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/crisp/parser.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Crisp
|
2
|
+
class Parser
|
3
|
+
Treetop.load(File.expand_path(File.join(File.dirname(__FILE__), 'crisp.treetop')))
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@parser = CrispParser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse(code)
|
10
|
+
ast = @parser.parse(code)
|
11
|
+
|
12
|
+
raise SyntaxError, "syntax error at : #{@parser.index}" if !ast
|
13
|
+
|
14
|
+
ast
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/crisp/shell.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Crisp
|
2
|
+
class Shell
|
3
|
+
require 'readline'
|
4
|
+
|
5
|
+
def run
|
6
|
+
runtime = Runtime.new
|
7
|
+
buffer = ''
|
8
|
+
|
9
|
+
while (line = Readline.readline(buffer.empty? ? ">> " : "?> ", true))
|
10
|
+
begin
|
11
|
+
buffer << line
|
12
|
+
ast = Parser.new.parse(buffer)
|
13
|
+
puts "=> " + runtime.run(ast).to_s
|
14
|
+
buffer = ''
|
15
|
+
rescue Crisp::SyntaxError => e
|
16
|
+
# noop
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "when evaluating arithmetic expressions, the language" do
|
4
|
+
include Crisp::SpecHelper
|
5
|
+
|
6
|
+
it "should calculate integer addition" do
|
7
|
+
evaluate("(+ 1 2)").should == 3
|
8
|
+
evaluate("(+ 12 13 1 5)").should == 31
|
9
|
+
evaluate("(+ 12 13 -1 -5)").should == 19
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should calculate integer substraction" do
|
13
|
+
evaluate("(- 3 1)").should == 2
|
14
|
+
evaluate("(- 14 1 4 8)").should == 1
|
15
|
+
evaluate("(- 2 5)").should == -3
|
16
|
+
evaluate("(- 2 -5)").should == 7
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should calculate integer multiplications" do
|
20
|
+
evaluate("(* 2 3)").should == 6
|
21
|
+
evaluate("(* 0 3)").should == 0
|
22
|
+
evaluate("(* 2 -3 1 10)").should == -60
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should calculate integer divisions" do
|
26
|
+
evaluate("(/ 12 3)").should == 4
|
27
|
+
evaluate("(/ 48 2 12)").should == 2
|
28
|
+
evaluate("(/ 30 -3 2)").should == -5
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should calculate nested arithmetic integer expressions" do
|
32
|
+
evaluate("(+ 1 2 (+ 2 3) (- 5 1)) ").should == 12
|
33
|
+
evaluate("(+ 1 2 (* 2 3) (/ 10 2)) ").should == 14
|
34
|
+
evaluate("(+ 1 2 (- 10 2 3) (* 1 2 3) (/ 12 4)) ").should == 17
|
35
|
+
evaluate("(/ 20 2 (+ 2 3) (- 5 3)) ").should == 1
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should calculate float addition" do
|
39
|
+
evaluate("(+ 1.0 2.)").should == 3.0
|
40
|
+
evaluate("(+ 12.5 13 1.4 5)").should == 31.9
|
41
|
+
evaluate("(+ 12. 13.5 -1.9 -5.1)").should == 18.5
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should calculate float substraction" do
|
45
|
+
evaluate("(- 3. 1.4)").should == 1.6
|
46
|
+
evaluate("(- 2 5.5)").should == -3.5
|
47
|
+
evaluate("(- 2.40 -5)").should == 7.4
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should calculate float multiplications" do
|
51
|
+
evaluate("(* 2.1 3.4)").should == 7.14
|
52
|
+
evaluate("(* 0 3.5)").should == 0
|
53
|
+
evaluate("(* 2 -3.1 1.9 10.1)").should == -118.978
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should calculate float divisions" do
|
57
|
+
evaluate("(/ 12.5 3.1)").should be_within(0.0000001).of(4.03225806451613)
|
58
|
+
evaluate("(/ 48 2.000 12.5)").should == 1.92
|
59
|
+
evaluate("(/ 30.0 -3 2.5)").should == -4.0
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should calculate nested arithmetic float expressions" do
|
63
|
+
evaluate("(+ 1 2.3 (* 2 3.5) (/ 10 4)) ").should == 12.3
|
64
|
+
evaluate("(+ 1.5 2 (- 10 2.4 3) (* 1.0 2 3) (/ 12 3.0)) ").should == 18.1
|
65
|
+
evaluate("(/ 20.4 2 (+ 2.5 3) (- 5.5 3)) ").should be_within(0.000000001).of(0.741818181818182)
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "the language" do
|
4
|
+
include Crisp::SpecHelper
|
5
|
+
|
6
|
+
it "should not bother whitespaces, tabs and linebreaks in statements" do
|
7
|
+
evaluate(" \r\t\n (\r+\t 1\n2 \t(\n - 9\r\t \n 2)\r)\r\t ").should == 10
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should print results" do
|
11
|
+
evaluate("(println (+ 1 1))")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should bind value to symbol" do
|
15
|
+
evaluate("(def bla 3)")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should not bind value to already binded symbol" do
|
19
|
+
lambda do
|
20
|
+
evaluate("(def name 123)(def name 123)")
|
21
|
+
end.should raise_error(Crisp::EnvironmentError, "name already binded")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise error if use def with wrong args" do
|
25
|
+
lambda do
|
26
|
+
evaluate("(def bla 1 2)")
|
27
|
+
end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'def' (3 for 2)")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should evaluate a list of operations" do
|
31
|
+
lambda do
|
32
|
+
evaluate("(+ 1 2) (def foo 1) (def foo 2)")
|
33
|
+
end.should raise_error(Crisp::EnvironmentError, "foo already binded")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should produce syntax error on invalid syntax" do
|
37
|
+
lambda do
|
38
|
+
evaluate("(()")
|
39
|
+
end.should raise_error(Crisp::SyntaxError, "syntax error at : 0")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should use values of binded symbols in later statements" do
|
43
|
+
evaluate("(def bla 2) (* 4 bla)").should == 8
|
44
|
+
evaluate("(def bla (* 2 3)) (* 4 bla 2)").should == 48
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should parse an array" do
|
48
|
+
evaluate("(def bla [1 2 3])").size.should == 3
|
49
|
+
evaluate("(def bla [1 2 3])")[1].should == 2
|
50
|
+
evaluate("(def bla [1 2 foo])")[2].should == :foo
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should raise error if not providing right amount of parameters for function creation" do
|
54
|
+
lambda do
|
55
|
+
evaluate("(fn [] (+ 1 2) [])")
|
56
|
+
end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'fn' (3 for 2)")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should raise error if not providing proper argument list for function generation" do
|
60
|
+
lambda do
|
61
|
+
evaluate("(fn (+ 2 1) (+ 1 2))")
|
62
|
+
end.should raise_error(Crisp::ArgumentError, "no parameter list defined")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should raise error if not providing proper function body" do
|
66
|
+
lambda do
|
67
|
+
evaluate("(fn [] [])")
|
68
|
+
end.should raise_error(Crisp::ArgumentError, "no function body defined")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should create functions" do
|
72
|
+
evaluate("(fn [arg] (+ 2 arg))")
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should bind functions to symbols" do
|
76
|
+
evaluate("(def myfn (fn [arg] (+ 1 arg)))").class.name.should == "Crisp::Function"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should call functions" do
|
80
|
+
evaluate("(def myfn (fn [a b] (+ 1 1)))(myfn 1 2)").should == 2
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should use parameters when calling functions" do
|
84
|
+
evaluate("(def myfn (fn [a b] (+ a b)))(myfn 5 2)").should == 7
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should raise error on wrong amount of parameters" do
|
88
|
+
lambda do
|
89
|
+
evaluate("(def myfn (fn [a1 a2 a3] (+ 1 1)))(myfn 1)")
|
90
|
+
end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'myfn' (1 for 3)")
|
91
|
+
end
|
92
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
+
|
7
|
+
SPEC_ROOT = Pathname(__FILE__).dirname.expand_path
|
8
|
+
|
9
|
+
require SPEC_ROOT.parent + 'lib/crisp'
|
10
|
+
|
11
|
+
module Crisp
|
12
|
+
module SpecHelper
|
13
|
+
def parse(expr)
|
14
|
+
Crisp::Parser.new.parse(expr)
|
15
|
+
end
|
16
|
+
|
17
|
+
def evaluate(expr)
|
18
|
+
Crisp::Runtime.new.run(parse(expr))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: crisp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Markus Gerdes
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-29 00:00:00 +01:00
|
19
|
+
default_executable: crisp
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: treetop
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 4
|
33
|
+
- 0
|
34
|
+
version: 1.4.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 7
|
46
|
+
segments:
|
47
|
+
- 2
|
48
|
+
- 2
|
49
|
+
- 0
|
50
|
+
version: 2.2.0
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
description:
|
54
|
+
email: github@mgsnova.de
|
55
|
+
executables:
|
56
|
+
- crisp
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
extra_rdoc_files:
|
60
|
+
- LICENSE
|
61
|
+
- README.md
|
62
|
+
files:
|
63
|
+
- CHANGELOG.md
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- bin/crisp
|
68
|
+
- crisp.gemspec
|
69
|
+
- lib/crisp.rb
|
70
|
+
- lib/crisp/chained_env.rb
|
71
|
+
- lib/crisp/crisp.treetop
|
72
|
+
- lib/crisp/env.rb
|
73
|
+
- lib/crisp/errors.rb
|
74
|
+
- lib/crisp/function.rb
|
75
|
+
- lib/crisp/functions.rb
|
76
|
+
- lib/crisp/functions/arithmetic.rb
|
77
|
+
- lib/crisp/functions/core.rb
|
78
|
+
- lib/crisp/nodes.rb
|
79
|
+
- lib/crisp/parser.rb
|
80
|
+
- lib/crisp/runtime.rb
|
81
|
+
- lib/crisp/shell.rb
|
82
|
+
- spec/crisp/arithmetics_spec.rb
|
83
|
+
- spec/crisp/basic_spec.rb
|
84
|
+
- spec/crisp/string_spec.rb
|
85
|
+
- spec/spec_helper.rb
|
86
|
+
has_rdoc: true
|
87
|
+
homepage: http://github.com/mgsnova/crisp
|
88
|
+
licenses: []
|
89
|
+
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
hash: 3
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
version: "0"
|
113
|
+
requirements: []
|
114
|
+
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 1.3.7
|
117
|
+
signing_key:
|
118
|
+
specification_version: 3
|
119
|
+
summary: a tiny lisp-like language written in ruby using treetop.
|
120
|
+
test_files:
|
121
|
+
- spec/crisp/arithmetics_spec.rb
|
122
|
+
- spec/crisp/basic_spec.rb
|
123
|
+
- spec/crisp/string_spec.rb
|
124
|
+
- spec/spec_helper.rb
|