if?{...}else?{...} DSL syntax possible?

P

Phil Tomson

As part of a DSL, I'd like to define my own if...else...end syntax (it's
either that or figure out how to use ripper and actually parse Ruby code - I'd
prefer to avoid that if possible).

Of course, first off you cant define an 'if' or 'else' method:

def if
...
end #won't work because if is 'reserved'

so I'll have to define an if? method instead:

def if?(boolean, &b)
if boolean
b.call
end
end

Seems pretty straightforward... but then assuming you could define an 'else?'
method, how would you define things so you could do:

if?(a==10){ puts "a is 10"} else? { puts "a is not 10"} #this

without '.'s as in:
if?(a==10){puts "a is 10}.else?{ puts "a is not 10 } #don't want this

Is it possible?

Of course the 'if?' method would have to change as well to accomodate the
'else?'

Maybe something like:
def if?(boolean, &b)
if boolean
b.call
end
boolean
end

with 'else?' being defined on TrueClass, FalseClass, NilClass:

class FalseClass
def else?(&b)
b.call
end
end

class TrueClass
def else?(&b)
#do nothing
end
end

class NilClass
def else?(&b)
b.call
end
end

but this still requires the '.' to work:
a=10
if?(a==10){puts "a is ten"}.else?{puts "a is not ten"} #=> a is ten
a=5
if?(a==10){puts "a is ten"}.else?{puts "a is not ten"} #=> a is not ten

Bonus points: Any way to get rid of the '.'s _and_ also allow:
if?(a==10){
puts "a is ten"
}
else? {
puts "a is not ten"
}

???

Phil
 
R

Robert Klemme

Phil Tomson said:
As part of a DSL, I'd like to define my own if...else...end syntax
(it's either that or figure out how to use ripper and actually parse
Ruby code - I'd prefer to avoid that if possible).

Of course, first off you cant define an 'if' or 'else' method:

def if
...
end #won't work because if is 'reserved'

so I'll have to define an if? method instead:

def if?(boolean, &b)
if boolean
b.call
end
end

Seems pretty straightforward... but then assuming you could define an
'else?' method, how would you define things so you could do:

if?(a==10){ puts "a is 10"} else? { puts "a is not 10"} #this

without '.'s as in:
if?(a==10){puts "a is 10}.else?{ puts "a is not 10 } #don't want this

Is it possible?

Of course the 'if?' method would have to change as well to accomodate
the 'else?'

Maybe something like:
def if?(boolean, &b)
if boolean
b.call
end
boolean
end

with 'else?' being defined on TrueClass, FalseClass, NilClass:

class FalseClass
def else?(&b)
b.call
end
end

class TrueClass
def else?(&b)
#do nothing
end
end

class NilClass
def else?(&b)
b.call
end
end

but this still requires the '.' to work:
a=10
if?(a==10){puts "a is ten"}.else?{puts "a is not ten"} #=> a is ten
a=5
if?(a==10){puts "a is ten"}.else?{puts "a is not ten"} #=> a is not
ten

Bonus points: Any way to get rid of the '.'s _and_ also allow:
if?(a==10){
puts "a is ten"
}
else? {
puts "a is not ten"
}

???

Phil

I'd first like to understand what's wrong with the built in if-then-else-end
that you need to do this. Can you elaborate?

Kind regards

robert
 
R

Ross Bamford

As part of a DSL, I'd like to define my own if...else...end syntax (it's
either that or figure out how to use ripper and actually parse Ruby code
- I'd
prefer to avoid that if possible).

Don't blame you. Not sure why you'd want this, though (built-in if is
pretty flexible, and check out case too), but just for the fun of it I
came up with this. Hold your nose, because it stinks a bit:

def if?(cond)
Thread.current[:__last_res] = yield if Thread.current[:__last_cond] =
cond
end

def else?
!Thread.current[:__last_cond] && Thread.current[:__last_res] or yield
end

a = 14

def is_ten(num)
if? num==10 do
"#{num} is ten"
end
else? do
"#{num} is not ten"
end
end

puts is_ten(10)
puts is_ten(15)

Although this is pretty nasty, it does at least retain the return
semantics of the standard if. Something's telling me theres a better way
to do this, maybe with continutations, but I'm not clever enough for that
:?

(P.s. RE. your last point, if you like parens and curly braces you could
have this instead):

if?(num==10) {
"#{num} is ten"
}
else? {
"#{num} is not ten"
}

Cheers,
 
P

Phil Tomson

I'd first like to understand what's wrong with the built in if-then-else-end
that you need to do this. Can you elaborate?

Sure, I need this DSL to not only be able to describe behaviour, but it also
must be translated into another existing language (VHDL). So either I have to
use something like ripper or ParseTree as a tool to parse the Ruby code, or I
can define some of my own constructs, such as the if?{...}else?{...}.
What will happen (if I can get it to work) is that if the DSL is being used in
simulation mode (depending on what modules are mixed in) it just runs like
running any Ruby program, but if I use it in translation mode, the
if?{...}else?{...} construct then translates itself into valid VHDL:

if?(a==10){ x << 0 }else?{x+=1}

would become:
--VHDL
if(a=10) then
x <= 0;
else
x <= x + 1;
end

Of course there's still a lot of details to work out yet...

Basically, I'm trying to avoid doing any kind of parsing for the translation,
if possible.

Phil
 
P

Phil Tomson

I'm afraid this won't be possible without actually parsing the code.
Reason being that you don't get access to the source code (for example for
the condition "a==10") during normal ruby execution.

Unless a is an object with an '==' method. (that's the situation I have,
actually). The '==' could be overriden to enable it to produce VHDL code.
Of course there's still a lot of details to work out yet...

Basically, I'm trying to avoid doing any kind of parsing for the
translation, if possible.

As I said, I don't think it's possible - at least not without major
complications. One thing that may or may not help is to facilitate
set_trace_func to record each function invocation. But that way you won't
see the if or else branch during execution. And you probably still have
the problem to access method argument values. No, I don't think you can
get away without parsing.

The alternative would be to use Ruby for a descriptive approach, i.e. do
describe code and actions with ruby source code and then create a data
structure from this that can be converted to actual ruby code and VHDL.

# just a rough idea
code = [
[:y, :=,
[:x, :+, 1],
],
[:call, :foo, :y],
# ...
]

Nah, this looks ugly...

Yup.

Basically I need the DSL to either:
1) execute
- or -
2) translate to VHDL
(execution mode, translation mode)
If I only allow certain types of objects in the DSL (doable, actually it's
kind of already that way) I can override lots of operator methods so that they
can produce VHDL code.

....of course, I still might end up having to parse the code, but I don't want
to give up yet ;-) Or it could be that I end up with a mix of both: some
minimal parsing in addition to the method overriding.

Phil
 
R

Robert Klemme

Phil said:
Unless a is an object with an '==' method. (that's the situation I
have, actually). The '==' could be overriden to enable it to produce
VHDL code.

What happens if someone writes "10 == a"? To me it looks as if the code
generation while execution would be too fragile / complicated. The
cleanest solution is probably to parse the DSL and emit VHDL and / or Ruby
(if it's not already Ruby).
Basically I need the DSL to either:
1) execute
- or -
2) translate to VHDL
(execution mode, translation mode)
If I only allow certain types of objects in the DSL (doable, actually
it's kind of already that way) I can override lots of operator
methods so that they can produce VHDL code.

...of course, I still might end up having to parse the code, but I
don't want to give up yet ;-) Or it could be that I end up with a
mix of both: some minimal parsing in addition to the method
overriding.

Well, at least you can stick with Ruby - there are parser implementations
for ruby code already.

Kind regards

robert
 
P

Phil Tomson

What happens if someone writes "10 == a"?

That's already an issue even in the simulation context. 'coerce' is your
friend in these situations. Since 'a' is likely to be a member of one of
three or four classes it's not too difficult to get around this with coerce.
To me it looks as if the code
generation while execution would be too fragile / complicated.

It's not really code generation while executing (well, I guess it is in a
sense). The code would be executed in two distinct contexts:
1) simulation (runs like a normal Ruby program)
2) translation : well, it still runs like a normal Ruby program, but instead
of, for example, the 'if?' method doing some conditional call on the passed in
block, the 'if?' method reports itself and executes the block (full of other
objects/methods that report themselves) and the 'else?' executes as well.

I sent another reply yesterday using some code to explain, but it seems to
have not made it to the list.

Basically, there would be two modules: Simulation (for the simulation context)
and ToVHDL (for the translation context). Depending on which module was
mixed-in to the DSL's class, you're either going to Simulate or Translate.
(that's a bit simplistic, there would actually need to be two different
collections of modules, but you get the idea). In the ToVHDL module(s) things
like operators report themselves, like so:
def ==(other)
if other.respond_to? :name
"#{name} = #{other.name}"
else #immediate value
"#{name} = #{other}"
end
end


Oh, if only 'if', 'else', 'while' and 'case' were just methods in Ruby that
would make things a lot easier ;-)
The
cleanest solution is probably to parse the DSL and emit VHDL and / or Ruby
(if it's not already Ruby).

I'm still not giving up yet ;-) but yes, I may end up having to do that.
Like I said, it might be possible to do a mix of the two somehow.
Well, at least you can stick with Ruby - there are parser implementations
for ruby code already.

Yeah, there are, but they always lag behind the current Ruby implementation.
Whenever there are changes to Ruby they don't show up right away. Also, I
think this is an interesting exercise to see if I can avoid using a parser.

....though, I'm casting envious glances over at Io at this point. This would
all be pretty trivial in Io it seems (partly because in Io things like 'if',
'else' 'while', etc. _are_ methods and partly because you have access to the
AST at any time.

Phil
 
T

Timothy Goddard

Take a look at ruby2c and see whether that can be adapted. I think it
works by analysing the ASTs that the ruby code gets parsed into. It
should be as easy as any other method of converting to rewrite this
into a ruby2vhdl program.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,785
Messages
2,569,624
Members
45,318
Latest member
LuisWestma

Latest Threads

Top