lucio 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest ADDED
@@ -0,0 +1,3 @@
1
+ Autotest.add_hook(:initialize) do |at|
2
+ at.add_exception %r{coverage}
3
+ end
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm use 1.9.2
2
+ rvm gemset use lucio
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'polyglot'
4
+ gem 'treetop'
5
+
6
+ gem 'simplecov', '>= 0.4.0', :require => false, :group => :test
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development, :test do
11
+ gem 'rspec'
12
+ gem 'ZenTest'
13
+ gem 'diff-lcs'
14
+ gem "yard", "~> 0.6.0"
15
+ gem "bundler", "~> 1.0.0"
16
+ gem "jeweler", "~> 1.6.4"
17
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,41 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ ZenTest (4.5.0)
5
+ diff-lcs (1.1.2)
6
+ git (1.2.5)
7
+ jeweler (1.6.4)
8
+ bundler (~> 1.0)
9
+ git (>= 1.2.5)
10
+ rake
11
+ polyglot (0.3.1)
12
+ rake (0.9.2)
13
+ rspec (2.6.0)
14
+ rspec-core (~> 2.6.0)
15
+ rspec-expectations (~> 2.6.0)
16
+ rspec-mocks (~> 2.6.0)
17
+ rspec-core (2.6.4)
18
+ rspec-expectations (2.6.0)
19
+ diff-lcs (~> 1.1.2)
20
+ rspec-mocks (2.6.0)
21
+ simplecov (0.4.2)
22
+ simplecov-html (~> 0.4.4)
23
+ simplecov-html (0.4.5)
24
+ treetop (1.4.9)
25
+ polyglot (>= 0.3.1)
26
+ yard (0.6.8)
27
+
28
+ PLATFORMS
29
+ ruby
30
+ x86-mingw32
31
+
32
+ DEPENDENCIES
33
+ ZenTest
34
+ bundler (~> 1.0.0)
35
+ diff-lcs
36
+ jeweler (~> 1.6.4)
37
+ polyglot
38
+ rspec
39
+ simplecov (>= 0.4.0)
40
+ treetop
41
+ yard (~> 0.6.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,15 @@
1
+ Open Source Initiative OSI - The MIT License (MIT):Licensing
2
+
3
+ The MIT License (MIT)
4
+ Copyright (c) 2011 P Balduino, R Lorca
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
7
+ to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
15
+ DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Lucio (en)
2
+
3
+ ## What?
4
+ Lucio is **intended to be** a Lisp-like language developed in Ruby only for knowledge and fun. No profit intended.
5
+
6
+ ## Why?
7
+ From Latin, Lucius means _Light_, because this development is enlightening my (poor) knowledge about programming languages, parsing and related subjects.
8
+
9
+ Lucio is also the name of one of the most important teachers I had, and I found this as a kind of homage.
10
+
11
+ The last, but not least, reason is to practice TDD, since I'm developing all the code using test-first.
12
+
13
+ ## When?
14
+ Lucio is developed in my almost nonexistent spare time as a way to keep me learning and passionate with software development.
15
+
16
+ ## Who?
17
+ I'm just an ordinary software developer, passionate and curious. If you want to collaborate, fork this and start to code. Simple.
18
+
19
+ ## Small sample
20
+ (/ (* (+ 1 2) (+3 4)) 2)
21
+ => 10.5
22
+
23
+ (eql? (* (+ 1 2) 3) 9)
24
+ => true
25
+
26
+ (if (eql? 10.5 (/ (* (+ 1 2) (+3 4)) 2))
27
+ ("great"))
28
+ => "great"
29
+
30
+ (if (eql? 0 (/ (* (+ 1 2) (+3 4)) 2))
31
+ ("great")
32
+ ("ouch")
33
+ => "ouch"
34
+
35
+ # Lucio (pt\_BR)
36
+
37
+ ## O que é?
38
+ Lucio **tem a intenção de ser** um dialeto Lisp desenvolvido inicialmente em Ruby apenas por diversão e aprendizado, sem qualquer intenção de lucro financeiro.
39
+
40
+ ## Por que?
41
+ Em Latim, Lucius significa _Luz_, ou _Iluminação_, porque esse projeto está iluminando meu (pobre) conhecimento sobre linguagens de programação, interpretadores e assuntos relacionados.
42
+
43
+ Lucio é também o nome de um dos mais importantes professores que já tive, e encontrei nesse projeto uma forma de homenageá-lo.
44
+
45
+ O último motivo, mas não menos importante, é poder praticar TDD, partindo do princípio que estou desenvolvendo **todo** o código utilizando essa metodologia.
46
+
47
+ ## Quando?
48
+ Lucio está sendo desenvolvido em meu quase inexistente tempo livre como uma forma de me manter sempre aprendendo coisas novas e motivado a continuar desenvolvendo.
49
+
50
+ ## Quem?
51
+ Sou apenas um rapaz latino-americano sem dinheiro no bolso nem amigos importantes, mas que realmente adora o que faz e muito curioso. Se você quiser participar e colaborar, crie um fork e comece a codificar. Simples assim.
52
+
53
+ ## Exemplos simples
54
+ (/ (* (+ 1 2) (+3 4)) 2)
55
+ => 10.5
56
+
57
+ (eql? (* (+ 1 2) 3) 9)
58
+ => true
59
+
60
+ (if (eql? 10.5 (/ (* (+ 1 2) (+3 4)) 2))
61
+ ("great"))
62
+ => "great"
63
+
64
+ (if (eql? 0 (/ (* (+ 1 2) (+3 4)) 2))
65
+ ("great")
66
+ ("ouch")
67
+ => "ouch"
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "lucio"
18
+ gem.homepage = "https://github.com/pbalduino/lucio"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Lucio is a LISP-like language created just for fun}
21
+ gem.description = %Q{Lucio is intended to be a Lisp-like language developed in Ruby only for knowledge and fun.}
22
+ gem.email = "pbalduino+github@gmail.com"
23
+ gem.authors = ["pbalduino"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'yard'
42
+ YARD::Rake::YardocTask.new
data/TODO.md ADDED
@@ -0,0 +1,32 @@
1
+ # TODO
2
+
3
+ ## Introdução
4
+
5
+ 1. Toda a comunicação será feita através desse arquivo, das mensagens de commit e do código.
6
+
7
+ 1. Dúvidas, intenções e próximos passos serão incluidos aqui.
8
+
9
+ 1. Commits mal explicados ou sem uma mensagem clara e objetiva serão revertidos.
10
+
11
+ 1. ???
12
+
13
+ 1. Profit
14
+
15
+ ## Tarefas
16
+
17
+ * Podemos fazer de duas formas: apagar as linhas de mensagens que foram resolvidas ou podemos simplesmente <strike>riscá-las</strike> e mantê-las por aqui, como uma forma rudimentar de histórico para quem não quiser/puder ver o versionamento do arquivo. O quê você prefere?
18
+
19
+ * Escrever testes para casos especiais ou que ainda levantem dúvidas sobre o funcionamento do parser.
20
+
21
+ * Finalizada uma versão funcional do parser, criar testes para resolver pequenas expressões matemáticas e fazer com que o Runner as execute.
22
+
23
+ * Minha primeira idéia foi de usar a classe Lexicon como uma espécie de dicionário de funções e operadores, onde ficariam as funções existentes e as novas. O Runner vai executar a expressão correspondente ao operador utilizando valores simples da lista. Caso seja encontrada uma lista dentro da lista atual, ela será passada recursivamente ao Runner até que retorne um valor simples.
24
+
25
+ * Exemplo:
26
+ <pre>
27
+ (eql? (+ 1 2) 3)
28
+ |
29
+ +--> runner: eql? (lista) 3 +-> eql? 3 3 -> true
30
+ | |
31
+ +-----> runner + 1 2 --> returns 3 --+
32
+ </pre>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.7
data/bin/lucio ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ARGV.empty?
4
+ $LOAD_PATH << (File.expand_path(File.dirname(__FILE__)) + '/../bin')
5
+ require('repl')
6
+ Lucio::Repl.run
7
+ else
8
+ puts 'meh'
9
+ end
data/bin/repl.rb ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << (File.expand_path(File.dirname(__FILE__)) + '/../lib')
3
+
4
+ require 'rubygems'
5
+ require 'lucio'
6
+
7
+ QUIT = '.q'
8
+
9
+ module Lucio
10
+ class Repl
11
+ def self.run
12
+ exit = false
13
+
14
+ puts <<lisp
15
+ # lucio interactive console
16
+ -------------------------
17
+ # type a lucio expression and press ENTER
18
+ # type .q and press ENTER to exit
19
+
20
+ lisp
21
+ while !exit
22
+ print 'lucio: '
23
+ command = (gets).chomp
24
+ exit = (command == QUIT)
25
+ unless exit
26
+ begin
27
+ print '=> '
28
+ p Lucio.eval(command)
29
+ rescue Exception => msg
30
+ puts "Error: #{msg}"
31
+ end
32
+ puts ''
33
+ end
34
+ end
35
+
36
+ puts '# bye!'
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,37 @@
1
+ require 'lucio'
2
+
3
+ module Lucio
4
+ class Grammar < Treetop::Runtime::SyntaxNode
5
+ def eval
6
+ Lucio::Runner.run make_tree(elements)
7
+ end
8
+
9
+ private
10
+ def make_tree(el)
11
+ x = make_list(el)
12
+ h = nil
13
+ h, x = Lucio.behead(x) if x[0] == '\''
14
+
15
+ Kernel::eval("Lucio::List.new(tree = #{x.inject('') {|x, y| x += "'#{y}',"}.strip.chop.gsub(/'\(',/, '[').gsub(/[,]?'\)'/, ']')}, evaluable = #{!h})")
16
+ end
17
+
18
+ def make_list(el, list = [])
19
+ el.each do |e|
20
+ methods = e.public_methods.reject{|i| i.to_s.chr != 'v'}.map{|i| i.to_s}.sort
21
+ if methods.include?('value')
22
+ list << e.value
23
+ else
24
+ unless e.empty? || e.text_value.strip.empty?
25
+ if e.nonterminal?
26
+ list = make_list(e.elements, list)
27
+ else
28
+ list << e.text_value
29
+ end
30
+ end
31
+ end
32
+ end
33
+ list
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,25 @@
1
+ require 'lucio/operators'
2
+
3
+ module Lucio
4
+ class Lexicon
5
+ def initialize
6
+ fill_operator_list
7
+ end
8
+
9
+ def get(operator)
10
+ ret = @operator_list[operator]
11
+ end
12
+
13
+ private
14
+ def fill_operator_list
15
+ @operator_list = {}
16
+ @operator_list['+'] = Operator::Sum.new
17
+ @operator_list['*'] = Operator::Multiplication.new
18
+ @operator_list['/'] = Operator::Division.new
19
+ @operator_list['-'] = Operator::Subtraction.new
20
+ @operator_list['eql?'] = Operator::Equality.new
21
+ @operator_list['if'] = Operator::Conditional.new
22
+ @operator_list['let'] = Operator::Attribution.new
23
+ end
24
+ end
25
+ end
data/lib/lucio/list.rb ADDED
@@ -0,0 +1,22 @@
1
+ module Lucio
2
+ class List
3
+ attr :tree, :evaluable
4
+
5
+ def initialize(tree, evaluable = true)
6
+ @tree = tree
7
+ @evaluable = evaluable
8
+ end
9
+
10
+ def to_s
11
+ flat(tree)
12
+ end
13
+
14
+ private
15
+ def flat(list)
16
+ result = "#{'(' if list.empty?}#{list.inject('(') do |result, item|
17
+ result += "#{item} "
18
+ end.chop})"
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ require 'lucio/runner'
2
+ require 'lucio/lexicon'
3
+ require 'lucio/list'
4
+ require 'polyglot'
5
+ require 'treetop'
6
+ require 'lucio_syntax'
7
+
8
+ module Lucio
9
+ def self.parse(str, debug = false)
10
+ parser = LucioParser.new
11
+ result = parser.parse str
12
+
13
+ puts "\n#{parser.failure_reason}" unless result || !debug
14
+
15
+ result
16
+ end
17
+
18
+ def self.eval(str)
19
+ parse(str).eval
20
+ end
21
+
22
+ def self.literal(str)
23
+ Lucio.eval(str).to_s
24
+ end
25
+
26
+ def self.behead(list)
27
+ [list[0], list.drop(1)]
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ require 'lucio/operators/macro'
2
+
3
+ module Lucio
4
+ module Operator
5
+ class Attribution < Macro
6
+ def execute(lexicon, *items)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ require 'lucio/operators/macro'
2
+
3
+ module Lucio
4
+ module Operator
5
+ class Conditional < Macro
6
+ def execute(lexicon, list)
7
+ tree = list.tree
8
+ condition = Lucio::Runner.run List.new(tree[0]), lexicon
9
+
10
+ if condition
11
+ Lucio::Runner.run List.new(tree[1]), lexicon
12
+ else
13
+ Lucio::Runner.run(List.new(tree[2]), lexicon) if tree.size > 2
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+
@@ -0,0 +1,16 @@
1
+ require 'lucio/operators/function'
2
+
3
+ module Lucio
4
+ module Operator
5
+ class Division < Function
6
+ def execute(lexicon, *items)
7
+ if items.size == 0
8
+ 0
9
+ else
10
+ items.inject {|result, item| result = result / item.to_f }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,11 @@
1
+ require 'lucio/operators/function'
2
+
3
+ module Lucio
4
+ module Operator
5
+ class Equality < Function
6
+ def execute(lexicon, *items)
7
+ items[0] == items[1]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Lucio
2
+ module Operator
3
+ class Macro
4
+ attr :type
5
+
6
+ def initialize
7
+ @type = :macro
8
+ end
9
+
10
+ def execute(*params)
11
+ raise "Operator not implemented"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ require 'lucio/operators/function'
2
+
3
+ module Lucio
4
+ module Operator
5
+ class Multiplication < Function
6
+ def execute(lexicon, *items)
7
+ if items.size == 0
8
+ 0
9
+ else
10
+ items.inject(1) {|multiplication, item| multiplication *= item }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,29 @@
1
+ module Lucio
2
+ module Operator
3
+ class Operator
4
+ attr :type
5
+
6
+ def type
7
+ @type
8
+ end
9
+
10
+ def execute(*items)
11
+ raise "Incomplete operator"
12
+ end
13
+ end
14
+
15
+ class Function
16
+ def initialize
17
+ super.type = :function
18
+ end
19
+ end
20
+
21
+ class Macro
22
+ def initialize
23
+ super.type = :macro
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ require 'lucio/operators/function'
2
+
3
+ module Lucio
4
+ module Operator
5
+ class Subtraction < Function
6
+ def execute(lexicon, *items)
7
+ p lexicon, items
8
+ if items.size == 0
9
+ 0
10
+ else
11
+ head, tail = Lucio.behead(items)
12
+ tail.empty? ? (head * -1) : tail.inject(head) {|subtraction, item| subtraction -= item }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,13 @@
1
+ module Lucio
2
+ module Operator
3
+ class Sum < Function
4
+ def execute(lexicon, *items)
5
+ if items.size == 0
6
+ 0
7
+ else
8
+ items.inject(0) {|sum, item| sum += item }
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'lucio/operators/operator'
2
+
3
+ require 'lucio/operators/sum'
4
+ require 'lucio/operators/multiplication'
5
+ require 'lucio/operators/division'
6
+ require 'lucio/operators/subtraction'
7
+ require 'lucio/operators/equality'
8
+ require 'lucio/operators/conditional'
9
+ require 'lucio/operators/attribution'
@@ -0,0 +1,60 @@
1
+ module Lucio
2
+ class Runner
3
+ def self.run(obj_tree, lexicon = Lexicon.new)
4
+ tree = obj_tree.tree
5
+
6
+ if obj_tree.evaluable
7
+ case
8
+ when tree.empty?
9
+ nil
10
+ when tree.size == 1
11
+ item = tree[0]
12
+ instruction = lexicon.get item
13
+ if instruction
14
+ instruction.execute lexicon
15
+ else
16
+ begin
17
+ eval(item)
18
+ rescue Exception => e
19
+ raise "Invalid or unknown symbol: #{item}", e.backtrace
20
+ end
21
+ end
22
+ else
23
+ operator, list = Lucio.behead(tree)
24
+ start, tail = Lucio.behead(list)
25
+
26
+ instruction = lexicon.get operator
27
+
28
+ raise "Invalid or unknown operator: #{operator}" unless instruction
29
+
30
+ if instruction.type == :function
31
+ if(start.kind_of? Array)
32
+ first = run List.new(start), lexicon
33
+ else
34
+ first = eval start
35
+ end
36
+
37
+ if tail.empty?
38
+ instruction.execute(lexicon, first)
39
+ else
40
+ tail.inject(first) do |result, item|
41
+ if item.kind_of? Array
42
+ i = run List.new(item), lexicon
43
+ else
44
+ i = eval item
45
+ end
46
+
47
+ instruction.execute(lexicon, result, i)
48
+ end
49
+ end
50
+
51
+ elsif instruction.type == :macro
52
+ instruction.execute lexicon, List.new(list)
53
+ end
54
+ end
55
+ else
56
+ obj_tree
57
+ end
58
+ end
59
+ end
60
+ end
data/lib/lucio.rb ADDED
@@ -0,0 +1 @@
1
+ require 'lucio/lucio'