crisp 0.0.5
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/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
|