poetics 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ %% name = Poetics::Parser
2
+
3
+ root = - value? - end
4
+
5
+ end = !.
6
+ - = (" " | "\t" | "\n")*
7
+
8
+ value = string
9
+ | number
10
+ | boolean
11
+
12
+
13
+ boolean = position (
14
+ true
15
+ | false
16
+ | null
17
+ | undefined)
18
+
19
+ true = "true" ~true_value
20
+ false = "false" ~false_value
21
+ null = "null" ~null_value
22
+ undefined = "undefined" ~undefined_value
23
+
24
+
25
+ number = position (
26
+ real
27
+ | hex
28
+ | int )
29
+
30
+ hexdigits = /[0-9A-Fa-f]/
31
+ hex = '0x' < hexdigits+ > ~hexadecimal(text)
32
+ digits = '0' | /[1-9]/ /[0-9]/*
33
+ int = < digits > ~number(text)
34
+ real = < digits '.' digits ('e' /[-+]/? /[0-9]/+)? > ~number(text)
35
+
36
+
37
+ string = position '"' < /[^\\"]*/ > '"' ~string_value(text)
38
+
39
+ # keep track of column and line
40
+ line = { current_line }
41
+ column = { current_column }
42
+ position = line:l column:c { position(l, c) }
@@ -0,0 +1,3 @@
1
+ require 'poetics/syntax/node'
2
+ require 'poetics/syntax/literal'
3
+ require 'poetics/syntax/ast'
@@ -0,0 +1,31 @@
1
+ module Poetics
2
+ module Syntax
3
+ def number(value)
4
+ Number.new line, column, value
5
+ end
6
+
7
+ def hexadecimal(value)
8
+ Number.new line, column, value.to_i(16)
9
+ end
10
+
11
+ def true_value
12
+ True.new line, column
13
+ end
14
+
15
+ def false_value
16
+ False.new line, column
17
+ end
18
+
19
+ def null_value
20
+ Null.new line, column
21
+ end
22
+
23
+ def undefined_value
24
+ Undefined.new line, column
25
+ end
26
+
27
+ def string_value(value)
28
+ String.new line, column, value
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,65 @@
1
+ module Poetics
2
+ module Syntax
3
+ class Value < Node
4
+ def to_sexp
5
+ [sexp_name, value, line, column]
6
+ end
7
+ end
8
+
9
+ class Number < Value
10
+ attr_accessor :value
11
+
12
+ def initialize(line, column, value)
13
+ super
14
+ @value = value.to_f
15
+ end
16
+
17
+ def sexp_name
18
+ :number
19
+ end
20
+ end
21
+
22
+ class Boolean < Node
23
+ def to_sexp
24
+ [sexp_name, line, column]
25
+ end
26
+ end
27
+
28
+ class True < Boolean
29
+ def sexp_name
30
+ :true
31
+ end
32
+ end
33
+
34
+ class False < Boolean
35
+ def sexp_name
36
+ :false
37
+ end
38
+ end
39
+
40
+ class Null < Boolean
41
+ def sexp_name
42
+ :null
43
+ end
44
+ end
45
+
46
+ class Undefined < Boolean
47
+ def sexp_name
48
+ :undefined
49
+ end
50
+ end
51
+
52
+ class String < Value
53
+ attr_accessor :value
54
+
55
+ def initialize(line, column, text)
56
+ super
57
+ @value = text
58
+ end
59
+
60
+ def sexp_name
61
+ :string
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,15 @@
1
+ module Poetics
2
+ module Syntax
3
+ class Node
4
+ attr_accessor :line, :column
5
+
6
+ def initialize(line, column, *)
7
+ @line = line
8
+ @column = column
9
+ end
10
+
11
+ def to_sexp
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Poetics
2
+ VERSION = "0.0.1"
3
+ RELEASE_DATE = "yyyy-mm-dd"
4
+ COMMAND_VERSION = "poetics #{VERSION} (1.1.0 #{RELEASE_DATE})"
5
+ end
data/poetics.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "poetics/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "poetics"
7
+ s.version = Poetics::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Brian Ford"]
10
+ s.email = ["brixen@gmail.com"]
11
+ s.homepage = "https://github.com/brixen/poetics"
12
+ s.summary = %q{A native implementation of CoffeeScript on the Rubinius VM}
13
+ s.description =<<-EOD
14
+ Poetics implements CoffeeScript (http://jashkenas.github.com/coffee-script/)
15
+ directly on the Rubinius VM (http://rubini.us). It includes a REPL for
16
+ exploratory programming, as well as executing CoffeeScript scripts directly.
17
+ EOD
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = Dir["spec/**/*.rb"]
21
+ s.executables = ["poetics"]
22
+ s.require_paths = ["lib"]
23
+ end
data/spec/custom.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'spec/custom/runner/relates'
2
+ require 'spec/custom/matchers/parse_as'
3
+ require 'spec/custom/utils/options'
4
+ require 'spec/custom/utils/script'
@@ -0,0 +1,21 @@
1
+ class ParseAsMatcher
2
+ def initialize(expected)
3
+ @expected = expected
4
+ end
5
+
6
+ def matches?(actual)
7
+ @actual = Poetics::Parser.parse_to_sexp actual
8
+ @actual == @expected
9
+ end
10
+
11
+ def failure_message
12
+ ["Expected:\n#{@actual.inspect}\n",
13
+ "to equal:\n#{@expected.inspect}"]
14
+ end
15
+ end
16
+
17
+ class Object
18
+ def parse_as(sexp)
19
+ ParseAsMatcher.new sexp
20
+ end
21
+ end
@@ -0,0 +1,97 @@
1
+ # NOTE: Copied from Rubinius
2
+ #
3
+ # SpecDataRelation enables concise specs that involve several different forms
4
+ # of the same data. This is specifically useful for the parser and compiler
5
+ # specs where the output of each stage is essentially related to the input
6
+ # Ruby source. Together with the #relates spec method, it enables specs like:
7
+ #
8
+ # describe "An If node" do
9
+ # relates "a if b" do
10
+ # parse do
11
+ # # return the expected sexp
12
+ # end
13
+ #
14
+ # compile do |g|
15
+ # # return the expected bytecode
16
+ # end
17
+ #
18
+ # jit do |as|
19
+ # # return the expected asm/machine code
20
+ # end
21
+ # end
22
+ #
23
+ # relates "if a; b; end" do
24
+ # # ...
25
+ # end
26
+ # end
27
+
28
+ class SpecDataRelation
29
+ # Provides a simple configurability so that any one or more of the possible
30
+ # processes can be run. See the custom options in custom/utils/options.rb.
31
+ def self.enable(process)
32
+ @processors ||= []
33
+ @processors << process
34
+ end
35
+
36
+ # Returns true if no process is specifically set or if +process+ is in the
37
+ # list of enabled processes. In other words, all processes are enabled by
38
+ # default, or any combination of them may be enabled.
39
+ def self.enabled?(process)
40
+ @processors.nil? or @processors.include?(process)
41
+ end
42
+
43
+ def initialize(ruby)
44
+ @ruby = ruby
45
+ end
46
+
47
+ # Formats the Ruby source code for reabable output in the -fs formatter
48
+ # option. If the source contains no newline characters, wraps the source in
49
+ # single quotes to set if off from the rest of the description string. If
50
+ # the source does contain newline characters, sets the indent level to four
51
+ # characters.
52
+ def format(ruby)
53
+ if /\n/ =~ ruby
54
+ lines = ruby.rstrip.to_a
55
+ if /( *)/ =~ lines.first
56
+ if $1.size > 4
57
+ dedent = $1.size - 4
58
+ ruby = lines.map { |l| l[dedent..-1] }.join
59
+ else
60
+ indent = " " * (4 - $1.size)
61
+ ruby = lines.map { |l| "#{indent}#{l}" }.join
62
+ end
63
+ end
64
+ "\n#{ruby}"
65
+ else
66
+ "'#{ruby}'"
67
+ end
68
+ end
69
+
70
+ # Creates spec example blocks if the compile process is enabled.
71
+ def compile(*plugins, &block)
72
+ return unless self.class.enabled? :compiler
73
+
74
+ ruby = @ruby
75
+ it "is compiled from #{format ruby}" do
76
+ generator = Rubinius::TestGenerator.new
77
+ generator.instance_eval(&block)
78
+
79
+ ruby.should compile_as(generator, *plugins)
80
+ end
81
+ end
82
+
83
+ def parse(&block)
84
+ return unless self.class.enabled? :parser
85
+
86
+ ruby = @ruby
87
+ it "is parsed from #{format ruby}" do
88
+ ruby.should parse_as(block.call)
89
+ end
90
+ end
91
+ end
92
+
93
+ class Object
94
+ def relates(str, &block)
95
+ SpecDataRelation.new(str).instance_eval(&block)
96
+ end
97
+ end
@@ -0,0 +1,21 @@
1
+ # Custom MSpec options
2
+ #
3
+ class MSpecOptions
4
+ def compiler
5
+ # The require is inside the method because this file has to be able to be
6
+ # loaded in MRI and there are parts of the custom ensemble that are
7
+ # Rubinius specific (primarily iseq, which could potentially be fixed by
8
+ # better structuring the compiler).
9
+ require 'spec/custom/runner/relates'
10
+
11
+ on("--compiler", "Run only the compile part of the compiler specs") do
12
+ SpecDataRelation.enable :compiler
13
+ end
14
+ end
15
+
16
+ def parser
17
+ on("--parser", "Run only the parse part of the compiler specs") do
18
+ SpecDataRelation.enable :parser
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ # Custom options for mspec-run
2
+ #
3
+ class MSpecRun
4
+ def custom_options(options)
5
+ options.compiler
6
+ options.parser
7
+ end
8
+ end
9
+
10
+ # Custom options for mspec-ci
11
+ #
12
+ class MSpecCI
13
+ def custom_options(options)
14
+ options.compiler
15
+ options.parser
16
+ end
17
+ end
18
+
19
+ # Custom options for mspec-tag
20
+ #
21
+ class MSpecTag
22
+ def custom_options(options)
23
+ options.compiler
24
+ options.parser
25
+ end
26
+ end
@@ -0,0 +1,6 @@
1
+ # vim: filetype=ruby
2
+ require 'spec/custom'
3
+
4
+ class MSpecScript
5
+ set :target, 'rbx'
6
+ end
@@ -0,0 +1,3 @@
1
+ $: << File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'poetics'
@@ -0,0 +1,60 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe "The Number node" do
4
+ relates "42" do
5
+ parse { [:number, 42.0, 1, 1] }
6
+ end
7
+
8
+ relates " 42" do
9
+ parse { [:number, 42.0, 1, 2] }
10
+ end
11
+
12
+ relates "42 " do
13
+ parse { [:number, 42.0, 1, 1] }
14
+ end
15
+
16
+ relates "1.23" do
17
+ parse { [:number, 1.23, 1, 1] }
18
+ end
19
+
20
+ relates "0x2a" do
21
+ parse { [:number, 42.0, 1, 1] }
22
+ end
23
+ end
24
+
25
+ describe "The True node" do
26
+ relates "true" do
27
+ parse { [:true, 1, 1] }
28
+ end
29
+ end
30
+
31
+ describe "The False node" do
32
+ relates "false" do
33
+ parse { [:false, 1, 1] }
34
+ end
35
+ end
36
+
37
+ describe "The Null node" do
38
+ relates "null" do
39
+ parse { [:null, 1, 1] }
40
+ end
41
+ end
42
+
43
+ describe "The Undefined node" do
44
+ relates "undefined" do
45
+ parse { [:undefined, 1, 1] }
46
+ end
47
+ end
48
+
49
+ describe "The String node" do
50
+ relates '"hello, world"' do
51
+ parse { [:string, "hello, world", 1, 1] }
52
+ end
53
+
54
+ relates <<-ruby do
55
+ "hello"
56
+ ruby
57
+
58
+ parse { [:string, "hello", 1, 7] }
59
+ end
60
+ end