Skip to content
This repository has been archived by the owner on Dec 24, 2023. It is now read-only.

Commit

Permalink
Fixed operator parsing
Browse files Browse the repository at this point in the history
- Fixed parsing of word operators (and, or..)
- Fixed tests
- Optimized parser_extensions performance
- Removed redundant code
  • Loading branch information
deathbeam committed Apr 23, 2016
1 parent fbb9fc9 commit 079db6e
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 80 deletions.
56 changes: 27 additions & 29 deletions lib/spoon/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def initialize
# Matches everything that starts with '#' until end of line
# example: # abc
rule(:comment) {
sym("#") >>
str("#") >>
stop
}

Expand All @@ -117,7 +117,7 @@ def initialize
(
chain_value >>
(
op(".") >>
trim(str(".")) >>
chain_value
).repeat(1)
).as(:chain)
Expand All @@ -135,6 +135,7 @@ def initialize
# example: return a, b, c
rule(:ret) {
key("return") >>
space.maybe >>
parens(expression_list).maybe.as(:return)
}

Expand All @@ -158,7 +159,7 @@ def initialize
# example: (a, b)
rule(:parameter_list) {
parameter >> (
op(",") >>
trim(str(",")) >>
parameter
).repeat
}
Expand All @@ -168,7 +169,7 @@ def initialize
rule(:parameter) {
word.as(:name) >>
(
op("=") >>
trim(str("=")) >>
expression.as(:value)
).maybe
}
Expand All @@ -178,36 +179,30 @@ def initialize
rule(:expression_list) {
expression >>
(
op(",") >>
trim(str(",")) >>
expression
).repeat
}

# Matches operator
rule(:operator) {
(
whitespace.maybe >>
match['\+\-\*\/%\^><\|&='].as(:op) >>
whitespace.maybe
) |
op(
[
"or",
"and",
"is",
"isnt",
"<=",
">=",
"!=",
"==",
"+=",
"-=",
"*=",
"/=",
"%=",
"or=",
"and="
]
str("<=") |
str(">=") |
str("!=") |
str("==") |
str("+=") |
str("-=") |
str("*=") |
str("/=") |
str("%=") |
str("and=") |
str("or=") |
key("or") |
key("and") |
key("is") |
key("isnt") |
match['\+\-\*\/%\^><\|&=']
).as(:op)
}

Expand All @@ -216,7 +211,7 @@ def initialize
rule(:closure) {
(
parens(parameter_list.as(:parameters)).maybe >>
op("->") >>
trim(str("->")) >>
body.as(:body)
).as(:closure)
}
Expand All @@ -227,7 +222,7 @@ def initialize
statement |
(
value.as(:left) >>
operator >>
trim(operator) >>
value.as(:right)
) | value
) >> endofline.maybe
Expand All @@ -238,6 +233,7 @@ def initialize
rule(:function) {
(
key("function") >>
space.maybe >>
word.as(:name) >>
space.maybe >>
function_body
Expand All @@ -258,12 +254,14 @@ def initialize
rule(:condition) {
(
key("if") >>
space.maybe >>
parens(expression.as(:body)) >>
space.maybe >>
body.as(:if_true) >>
(
space.maybe >>
key("else") >>
space.maybe >>
body.as(:if_false)
).maybe
).as(:condition)
Expand Down
56 changes: 11 additions & 45 deletions lib/spoon/util/parser_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,26 @@ module Spoon
module Util
# Monkey-patch the parser to include some common methods
class Parslet::Parser
# Stores string as key, matches it and then skips space after it
private def store_keyword(value)
@keywords = [] if @keywords.nil?
@keywords.push value unless @keywords.include? value

str(value)
def initialize
super
@keywords = []
end

# Matches only if you are not trying to match any previously stored key
rule(:skip_key) {
if @keywords.nil? or @keywords.empty?
always_match
else
result = str(@keywords.first).absent?

for keyword in @keywords
result = result >> str(keyword).absent?
end
result = str(@keywords.first).absent?

result
for keyword in @keywords
result = result >> str(keyword).absent?
end
}

# Matches string and skips space after it
def sym(value)
if value.kind_of?(Array)
result = str(value.first)
value.each { |val| result |= str(val) }
result >> space.maybe
else
str(value) >> space.maybe
end
end
result
}

# Matches keyword and skips space after it
# Stores string as key and matches it
def key(value)
if value.kind_of?(Array)
result = store_keyword(value.first)
value.each { |val| result |= store_keyword(val) }
result >> space.maybe
else
store_keyword(value) >> space.maybe
end
end

# Matches string or keyword, based on if it is word or not
def op(value)
if value.kind_of?(Array)
result = whitespace.maybe >> (/\w/.match(value.first) ? key(value.first) : sym(value.first))
value.each { |val| result |= (/\w/.match(val) ? key(val) : sym(val)) }
result >> whitespace.maybe
else
trim(/\w/.match(value) ? key(value) : sym(value))
end
@keywords.push value unless @keywords.include? value
str(value)
end

# Trims all whitespace around value
Expand Down
11 changes: 5 additions & 6 deletions spec/parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

describe Spoon::Parser do
let(:parser) { Spoon::Parser.new }
context "block parsing" do
subject { parser.block }

it { should parse "\n return this\n return that\n" }
end

context "call parsing" do
subject { parser.call }
Expand Down Expand Up @@ -66,12 +71,6 @@
it { should_not parse "function test me it he" }
end

context "indent parsing" do
subject { parser }

it { should parse "function hello(a)\n return this\n return that\n" }
end

context "number parsing" do
subject { parser.number }

Expand Down

0 comments on commit 079db6e

Please sign in to comment.