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.
@@ -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.
@@ -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
@@ -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
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'lib/crisp'
4
+
5
+ if ARGV.size == 0
6
+ Crisp::Shell.new.run
7
+ elsif ARGV.size == 1
8
+ ast = Crisp::Parser.new.parse(File.read(ARGV[0]))
9
+ Crisp::Runtime.new.run(ast).to_s
10
+ else
11
+ puts "to many args"
12
+ exit 1
13
+ end
@@ -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
+
@@ -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
@@ -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
@@ -0,0 +1,12 @@
1
+ module Crisp
2
+
3
+ class SyntaxError < StandardError
4
+ end
5
+
6
+ class ArgumentError < StandardError
7
+ end
8
+
9
+ class EnvironmentError < StandardError
10
+ end
11
+
12
+ end
@@ -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,11 @@
1
+ require 'crisp/functions/core'
2
+ require 'crisp/functions/arithmetic'
3
+
4
+ module Crisp
5
+ module Functions
6
+ def self.load(env)
7
+ Core.load(env)
8
+ Arithmetic.load(env)
9
+ end
10
+ end
11
+ 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
@@ -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
@@ -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
@@ -0,0 +1,12 @@
1
+ module Crisp
2
+ class Runtime
3
+ def initialize
4
+ @env = Env.new
5
+ Functions.load(@env)
6
+ end
7
+
8
+ def run(ast)
9
+ ast.eval(@env)
10
+ end
11
+ end
12
+ end
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe "when evaluating string expressions, the language" do
4
+ include Crisp::SpecHelper
5
+
6
+ it "should concat strings" do
7
+ evaluate('(+ "foo" "bar")').should == 'foobar'
8
+ end
9
+ end
@@ -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