Clojure code parsing - parsing

I'm writing a DSL and I want to implement debugging.
However, I lose all the line info because i read user input with (read-string the-input) and then it is no longer source, but s expressions which are evaluated with eval.
First thing I thought about is this, simply add code (without user's knowledge) at the end of each line, like so:
(user-func-a)
(user-func-b)
Would become:
(user-func-a) (my-source-index 0)
(user-func-b) (my-source-index 1)
However, this would fall apart in multi line form:
(+ (my-source-index 0)
1 (my-source-index 1)
2 (my-source-index 2)
3) (my-source-index 3)
Then I wanted to implement bracket tracking, so, that only when parens are closed I'd add source index:
(+
1
2
3) (my-source-index 3)
But then I thought, wait, what about comments? I have to track for ';' too or it'll be this:
(+
1 ;) (my-source-index 1)
2 (my-source-index 2)
3) (my-source-index 3)
Even if I implemented that i'd have to track where the comment symbol was not in quotes (part of the text) and so on.
I didn't go much further after that (there probably would be even more quirks...) and came to here. Is there any library for clojure to parse code, find out the context surrounding the line and to avoid such hassles?

The IndexingPushBackReader from clojure.tools.reader should do the job. According to documentation it adds line/column metadata to symbols, vectors and maps (but not literals).
See the following:
(require '[clojure.tools.reader :as r])
(require '[clojure.tools.reader.reader-types :as rt])
(def reader (rt/indexing-push-back-reader
"(+ \n;; comment here \n 1 \n a)"))
(def s-expr (r/read reader)) ;; => (+ 1 a)
(meta s-expr) ;; => {:line 1, :column 1, :end-line 4, :end-column 4}
(nth s-expr 0) ;; => +
(meta (nth s-expr 0)) ;; => {:line 1, :column 2, :end-line 1, :end-column 3}
(nth s-expr 1) ;; => 1
(meta (nth s-expr 1)) ;; => nil (no metadata on literals)
(nth s-expr 2) ;; => a
(meta (nth s-expr 2)) ;; => {:line 4, :column 2, :end-line 4, :end-column 3}

Related

Why does the (2, 3) map to the y value of a point instead of mapping 2 and 3 respectively to x and y value of that point? Common Lisp

I've been working on a jarvis march implementation for common lisp. The the jarvis march algorithm takes a bunch of points and returns the convex hull of that point cloud. I'm representing every point as a struct like this:
(defstruct point x y)
Then I went on to define a test set, but, since the notation to initialize a struct is rather long (make-point :x 0 :y 1), I decided to make a function which automatically did this for me:
(defun make-points (list)
(map
'list
(lambda (e) (make-point :x (first e) :y (second e)))
list))
Sadly enough it doesn't work.
(print (first (make-points '('(2 3))))) ;prints out '#S(POINT :X QUOTE :Y (2 3))' => wrong
(print (make-point :x 2 :y 3)) ;prints out '#S(POINT :X 2 :Y 3)' => correct
It binds the complete list (2 3) to the y value of all things and it assigns nothing to the x value. Why does it do this and how can I fix it.
Thanks in advance, I'm fairly new to lisp (as you might have been able to guess from this question) and I would be highly appreciative if someone could help me out. If anyone knows any shortcuts or has any good arguments against my workmethod that would also be nice, although it might be better to tell me them in the comments since they would not be directly answering the question.
CL-USER 1 > (defstruct point x y)
POINT
CL-USER 2 > (defun make-points (list)
(map
'list
(lambda (e) (make-point :x (first e) :y (second e)))
list))
MAKE-POINTS
Tracing it:
CL-USER 3 > (trace make-points)
(MAKE-POINTS)
CL-USER 4 > (make-points '('(2 3)))
0 MAKE-POINTS > ...
>> LIST : ((QUOTE (2 3)))
0 MAKE-POINTS < ...
<< VALUE-0 : (#S(POINT :X QUOTE :Y (2 3)))
(#S(POINT :X QUOTE :Y (2 3)))
Traditional print debug:
CL-USER 5 > (defun make-points (list)
(map
'list
(lambda (e)
(print (list :first (first e) :second (second e)))
(make-point :x (first e) :y (second e)))
list))
MAKE-POINTS
CL-USER 6 > (untrace make-points)
(MAKE-POINTS)
CL-USER 7 > (make-points '('(2 3)))
(:FIRST QUOTE :SECOND (2 3))
(#S(POINT :X QUOTE :Y (2 3)))
Change the input. The list is already quoted. No need to quote it twice.
CL-USER 8 > (make-points '((2 3)))
(:FIRST 2 :SECOND 3)
(#S(POINT :X 2 :Y 3))
CL-USER 9 >

i am trying to add a method each_run to the Ruby Array class which takes a code block expecting two arguments

Add a method each_run to the Ruby Array class which takes a code block expecting two arguments. It calls the code block once for each contiguous run of equal items in the array, sending the length of the run and the item repeated.the output will appear like this:
irb(main):001:0> load("eachrun.rb")
=> true
irb(main):002:0> [5,9,3,4,4,4,7,8,8].each_run { |n,x|
irb(main):003:1* print x, " appearing ", n, " times\n" }
5 appearing 1 times
9 appearing 1 times
3 appearing 1 times
4 appearing 3 times
7 appearing 1 times
8 appearing 2 times
=> nil
I would use group_by and yield the keys and size of the associated groups.
def each_run
group_by(&:itself).map {|k,v| yield v.length, k }
end
I wouldn't monkey-patch it directly into Array, though. First, it would make more sense as part of Enumerable, and second, monkey-patching can get messy. You could make it a refinement, though. Sadly, you can only refine classes, not modules, so it's back to Array instead of Enumerable:
module EachRun
refine Array do
def each_run
group_by(&:itself).map {|k,v| yield v.length, k }
end
end
end
Then just put using EachRun at the top of any code that you want to be able to use your new method.
I read the intention of the problem "It calls the code block once for each contiguous run of equal items in the array" as meaning the example list is a touch misleading since it contains no subsequent runs of a digit so that you can ignore the structure of the input list and just count how many times any given item appears. If the example had been:
[5,9,3,4,4,4,7,8,8,5,5]
Then printing "5 appears 3 times" doesn't suggest itself as correct.
I haven't written any Ruby in quite a while so this may not be optimal but it's what I came up with:
require 'test/unit/assertions'
include Test::Unit::Assertions
module Enumerable
def runs
if empty?
[]
else
runs = [[first,1]]
self[1..-1].each do |item|
if item == runs.last.first
runs = runs[0..-2] + [[item,runs.last.last+1]]
else
runs = runs + [[item,1]]
end
end
runs
end
end
def each_run(&block)
runs.each(&block)
end
end
assert_equal( [], [].runs )
assert_equal( [[5,1],[9,1],[3,1],[4,3],[7,1],[8,2],[5,2]], [5,9,3,4,4,4,7,8,8,5,5].runs )
[5,9,3,4,4,4,7,8,8,5,5].each_run do |m,n|
puts "#{m} appears #{n} times"
end
Outputting
> 5 appears 1 times
> 9 appears 1 times
> 3 appears 1 times
> 4 appears 3 times
> 7 appears 1 times
> 8 appears 2 times
> 5 appears 2 times
When I thought about it in Clojure what I came up with was this:
(defn runs [l]
(reduce (fn [memo [x n :as e]]
(let [[last-x last-n] (last memo)]
(if (= x last-x)
(conj (vec (drop-last memo)) [x (inc last-n)])
(conj memo e))))
[] (map (fn [e] [e 1]) l)))
(runs [5 5 9 8 1 1 4 3 3 3 3 5 5 5 2 2])
=> [[5 2] [9 1] [8 1] [1 2] [4 1] [3 4] [5 3] [2 2]]
This may give you a few ideas about how to proceed:
irb(main):001:0> a = [5,9,3,4,4,4,7,8,8]
=> [5, 9, 3, 4, 4, 4, 7, 8, 8]
irb(main):002:0> a.uniq.each { |e| printf("%d appears %d times\n", e, a.count(e)) }
5 appears 1 times
9 appears 1 times
3 appears 1 times
4 appears 3 times
7 appears 1 times
8 appears 2 times
I went with the suggest of using uniq and each but that's only if you need to return the uniq values as an array over all. There's lots of ways you can iterate it's up to you but the key here is monkey patching and yielding your values in order to use a block.
class Array
def each_run
uniq.each do |item|
yield(item, self.count(item))
end
end
end
[12] pry(main)> [5,9,3,4,4,4,7,8,8].each_run do |num, count|
[12] pry(main)* printf("%d appears %d times\n", num, count)
[12] pry(main)* end
5 appears 1 times
9 appears 1 times
3 appears 1 times
4 appears 3 times
7 appears 1 times
8 appears 2 times
=> [5, 9, 3, 4, 7, 8]
sources:
Monkey Patch Array Stack Overflow

How to use next with inject in ruby

I was writing some code like following:
[1,2,3,4,5].inject([]) do |res, a|
res << a*a and next if a == 2
res << a
end
It gives following error:
NoMethodError: undefined method `<<' for nil:NilClass
As by next it makes res variable as nil, How to work around this problem?
I tried various ways but couldn't get next to work with ruby, I know this snippet I have provided can be done without next(a == 4 ? res << a*a : res << a), but in my actual use-case I have some complex logic and can't be done that simply.
Replace the
res << a*a and next if a == 2
with
next res << a*a if a == 2
Now, it will work.
Example :-
#!/usr/bin/env ruby
ar = [1,2,3,4,5].inject([]) do |res, a|
next res << a*a if a == 2
res << a
end
p ar
# >> [1, 4, 3, 4, 5]
Read the documentation of next
next can take a value, which will be the value returned for the current iteration of the block....
Ugh. Don't use a trailing conditional for this. Instead it is easily done using a standard if/else:
[1,2,3,4,5].inject([]) do |res, a|
if a == 2
res << a*a
else
res << a
end
end
# => [1, 4, 3, 4, 5]
Any time you feel like you're coding yourself into a corner, don't look for a way out, instead, back up and look at what you're trying to accomplish to see if there is a more straightforward way there.
I'd probably tweak the code a bit more though, for readability. Long term maintenance relies on quickly understanding what is going on, and code that is convoluted or not obvious can take its toll later:
[1,2,3,4,5].inject([]) do |res, a|
if a == 2
res << a*a
else
res << a
end
res # return it for clarity in what the block is returning
end
# => [1, 4, 3, 4, 5]
inject is similar to each_with_object, only it relies on the accumulator being returned at the end of the block, which is why I would add the res at the end of the block for clarity, due to the if block. Switching to each_with_object removes that reliance on the return value of the block, allowing the following code to be more logically clear:
[1,2,3,4,5].each_with_object([]) do |a, ary|
if a == 2
ary << a*a
else
ary << a
end
end
# => [1, 4, 3, 4, 5]
Of course, at that point, the whole thing can be reduced further, and could take advantage of the ternary version using:
[1,2,3,4,5].each_with_object([]) do |a, ary|
a2 = (a == 2) ? a * a : a
ary << a2
end
# => [1, 4, 3, 4, 5]
Which of the above two are more readable is somewhat up to the person coding it and the person responsible for maintaining it. I'd lean toward the non-ternary version because it's more easily extended/expanded and doesn't have the line noise of the ternary ?: chain.
Since it was asked in the comments, map reduces some noise, and is how we should transform an array:
[1,2,3,4,5].map { |a|
(a == 2) ? a * a : a
}
That's untested but it looks correct.

What does "<<" exactly do in ruby?

I am new to Ruby, so I am still learning several things. But, I do have good experience with Java and C.
I would like to know what this does exactly:
[ 'a','b', 'c' ].each_with_index {|item, index| result << [item, index] }
Specifically, I am interested in the <<. Some research tells me that it is used for bit shifting, but it's obvious that is not the case here, so what is it doing here?
The << operator is adding items to the result array in this case.
See " how to add elements to ruby array (solved)".
In Ruby, all the things which are operators in C/Java, like +, -, *, /, and so on, are actually method calls. You can redefine them as desired.
class MyInteger
def +(other)
42 # or anything you want
end
end
Array defines the << method to mean "push this item on the end of this array". For integers, it's defined to do a bit shift.
Aside from Array, many other classes define << to represent some kind of "appending" operation.
It's the Array append operator.
<< is a method, and will do different things for different classes. Array uses it to push an object onto the end of an array. Fixnums use it to shift.
This is basically an Append Operator.
It was be used with to append either an element to an array or a substring to string
For Arrays
1.9.2-p290 :009 > arr = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
1.9.2-p290 :010 > arr << 6
=> [1, 2, 3, 4, 5, 6]
1.9.2-p290 :011 >
For Strings
1.9.2-p290 :011 > str = "ruby"
=> "ruby"
1.9.2-p290 :012 > str << 'rails'
=> "rubyrails"
1.9.2-p290 :013 >

Using a dynamic precision value in number_to_currency based on the decimal value

Thoughout our app we use number_to_currency(value, :precision => 2). However, we now have a requirement whereby the value may need displaying to three or more decimal places, e.g.
0.01 => "0.01"
10 => "10.00"
0.005 => "0.005"
In our current implementation, the third example renders as:
0.005 => "0.01"
What's the best approach for me to take here? Can number_to_currency be made to work for me? If not, how do I determine how many decimal places a given floating point value should be displayed to? sprintf("%g", value) comes close, but I can't figure out how to make it always honour a minimum of 2dp.
The following will not work with normal floats, because of precision problems, but if you're using BigDecimal it should work fine.
def variable_precision_currency(num, min_precision)
prec = (num - num.floor).to_s.length - 2
prec = min_precision if prec < min_precision
number_to_currency(num, :precision => prec)
end
ruby-1.8.7-p248 > include ActionView::Helpers
ruby-1.8.7-p248 > puts variable_precision_currency(BigDecimal.new("10"), 2)
$10.00
ruby-1.8.7-p248 > puts variable_precision_currency(BigDecimal.new("0"), 2)
$0.00
ruby-1.8.7-p248 > puts variable_precision_currency(BigDecimal.new("12.45"), 2)
$12.45
ruby-1.8.7-p248 > puts variable_precision_currency(BigDecimal.new("12.045"), 2)
$12.045
ruby-1.8.7-p248 > puts variable_precision_currency(BigDecimal.new("12.0075"), 2)
$12.0075
ruby-1.8.7-p248 > puts variable_precision_currency(BigDecimal.new("-10"), 2)
$-10.00
ruby-1.8.7-p248 > puts variable_precision_currency(BigDecimal.new("-12.00075"), 2)
$-12.00075

Resources