[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

C DSL anyone?

Brad Phelan

5/2/2007 4:15:00 PM

Just curious,

is anybody working on a C language DSL that could generate C code. I'm
kinda interested in how it may be possible to use RSpec to unit test C
code in a nice way.

My first attempt at a C DSL allows the following example.


require 'cexpression.rb'

code = CTools::CCode.new

code = CTools::CCode.open do

_decl :a, :ubyte2
_decl :b, :ubyte2
_decl :c, :ubyte2

_typedef :Point do
_struct do
_decl :x, :ubyte4
_decl :y, :ubyte2
_decl :z, :ubyte1
end
end

_decl :point, :Point

_for(a.assign(0), a < 1, a.inc(1)) do
_while(b < 1) do
_for(c.assign(0), c < 1, c.inc(1)) do
_let b, b + 1
_printf "%d %d %d", point.x, point.y, point.z
end
end
end
end

puts code

# generates

ubyte2 a;
ubyte2 b;
ubyte2 c;
typedef struct {
ubyte4 x;
ubyte2 y;
ubyte1 z;
}Point;
Point point;
for (a = 0; a < 1; a += 1){
while (b < 1){
for (c = 0; c < 1; c += 1){
b = b + 1
printf("%d %d %d", point.x, point.y, point.z );
}
}
}


===========================
library cexpression.rb is quite simple
===========================

module CTools

# Indent a block of C code

def CTools.c_indent(text)
indent_text = "
"
indent = 0;
cont = false;
out_text = []
text.each_line do |line|
line.gsub!(/^\s*/,"")
if line =~ /\{.*\}/
line.gsub!(/^/,indent_text[1..indent])
else
if line =~ /\}/
indent = indent - 3
end
line.gsub!(/^/,indent_text[1..indent])
if line =~ /\{/
indent = indent + 3
end
# Convert "/**/" into blank lines
line.gsub!(/^\s*\/\*\*\//,'')
end
# Indent on unmatched round brackets
indent = indent + ( line.count("(") - line.count(")") ) * 3
# Indent on backslash continuation
if cont
if line !~ /\\$/
indent = indent - 6
cont = false
end
else
if line =~ /\\$/
indent = indent + 6
cont = true
end
end
out_text << line
end
out_text.join
end

class CExpr

# Operator
attr_reader :op

# Arguments ( Sub expressions )
attr_reader :args

def initialize(*args)
@args = args
end

def to_s
"#{@op}(" + args.collect { |a| a.to_s }.join(', ') + ")"
end

##### Operators and Calls ##########


def assign(val)
method_missing "=", val
end

def inc( val )
method_missing "+=", val
end

def decr( val )
method_missing "-=", val
end

def method_missing(meth_id, *args)
case meth_id.to_s
when '=', '+=', '-=', '>=', '<=', '<', '>', '+', '-', '*', '/'
BinOp.new(meth_id, self, *args)
when '[]'
ArrayOp.new(self, *args)
else
BinOp.new(".", self, CExpr.new(meth_id, *args))
end
end

end

class BinOp < CExpr
def initialize(op, *args)
@op = op
@args = args
if args.length != 2
raise :BadNumberOfArgs
else
end

end

def to_s
case @op
when '.'
"(#{args[0]}.#{CTools.debracket(args[1].to_s)})"
else
"(#{args[0]} #{@op} #{args[1]})"
end
end
end

class ArrayOp < CExpr
def initialize(op, *args)
@op = op
@args = args
end

def to_s
"#{op}[" + args.join(', ') + "]"
end
end


class CVar < CExpr
attr :name
attr :type

def initialize(name, type)
@name = name;
@type = type;
end

def decl
"#{type} #{name};"
end

def to_s
name.to_s
end

end

def self.debracket(str)
(str.gsub(/^\(/,'')).gsub(/\)$/,'')
end

class BlankSlate
instance_methods.each { |m|
case m
when /^__/
when /instance_eval/
else
undef_method m
end
}
end

class CCode
private

def initialize
@buffer = ""
end

def new
end

public

def self.open &block
code = CCode.new
code.instance_eval &block
code.to_s
end

def method_missing(meth, *args)
@buffer << meth.to_s.gsub(/^_/,'') << "(" << args.collect{ |a|
case a
when String
# Literal strings are output quoted
'"' + a + '"'
else
# Other objects are considered names
CTools.debracket(a.to_s)
end
}.join(', ') << " );\n"
end


def scope( lf=true)
@buffer << "{\n"
yield
@buffer << "}"
@buffer << "\n" if lf
end

def <<( e )

s = CTools::debracket(e.to_s)

@buffer << s << ";\n"
end

def _if(cond, &block)
@buffer << "if (#{cond})"
scope &block
end

def _else(&block)
@buffer << "else"
scope &block
end

def _let(a, b)
@buffer << "#{a} = #{CTools.debracket(b.to_s)}\n"
end

def _for(init, cond, inc, &block)
init = CTools.debracket(init.to_s)
cond = CTools.debracket(cond.to_s)
inc = CTools.debracket(inc.to_s)

@buffer << "for (#{init}; #{cond}; #{inc})"
scope &block
end

def _while(cond, &block)
cond = CTools.debracket(cond.to_s)
@buffer << "while (#{cond})"
scope &block
end

def _typedef name, &block
@buffer << "typedef "
yield
@buffer << name.to_s << ";\n"
end

def _struct (name="", &block)
@buffer << "struct #{name}"
# Evaluate the struct declarations in a new
# scope
@buffer << CTools::CCode.open do
scope false do
instance_eval &block
end
end
@buffer << ";\n" if name != ""
end

# Declare a variable in scope and
# add an instance method to retrieve
# the symbol
def _decl name, type="void *"
var = CVar.new(name, type)
@buffer << var.decl << "\n"
self.class.send(:define_method, name){ var }
end

def to_s
CTools.c_indent @buffer
end
end
end
13 Answers

Ara.T.Howard

5/2/2007 5:00:00 PM

0

Joel VanderWerf

5/2/2007 6:25:00 PM

0

Brad Phelan wrote:
> Just curious,
>
> is anybody working on a C language DSL that could generate C code. I'm
> kinda interested in how it may be possible to use RSpec to unit test C
> code in a nice way.

Here's one:

http://raa.ruby-lang.org/project/c...
http://rb-cgen.darwin...

There's an example at:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-t...

IMO, cgen's shadow class mechanism is what differentiates cgen from
RubyInline. (Shadow classes give you an easy way of defining and
inheriting T_DATA structs as if they were just part of your ruby
classes, using the shadow_attr_accessor methods.) RubyInline is probably
more sophisticated in many ways (compiler options, storing intermediate
code securely, availability as a gem).

I've been using cgen since 2001 for boosting the performance of
numerical integration and simulations. I've also used it to wrap some
libraries that I didn't want to bother with swig for.

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

SonOfLilit

5/3/2007 7:19:00 AM

0

Am I the only one that thinks OP is looking for a library to assist in
generating ascii C code, like Markaby does for HTML, and not for
executing C code that you wrote as a string?

Brad, I don't think you'll find one, and in fact, I don't think you'll
need one. Why? C has so much syntax that you're better off generating
it with string manipulation than with a DSL.

HTML is so simple that there was more gain (succintness,
automatability) than loss (new language to learn) in Markaby and it's
neighbours. With C, I don't see such gains overcoming the loss.

Aur

On 5/2/07, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:
> Brad Phelan wrote:
> > Just curious,
> >
> > is anybody working on a C language DSL that could generate C code. I'm
> > kinda interested in how it may be possible to use RSpec to unit test C
> > code in a nice way.
>
> Here's one:
>
> http://raa.ruby-lang.org/project/c...
> http://rb-cgen.darwin...
>
> There's an example at:
>
> http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-t...
>
> IMO, cgen's shadow class mechanism is what differentiates cgen from
> RubyInline. (Shadow classes give you an easy way of defining and
> inheriting T_DATA structs as if they were just part of your ruby
> classes, using the shadow_attr_accessor methods.) RubyInline is probably
> more sophisticated in many ways (compiler options, storing intermediate
> code securely, availability as a gem).
>
> I've been using cgen since 2001 for boosting the performance of
> numerical integration and simulations. I've also used it to wrap some
> libraries that I didn't want to bother with swig for.
>
> --
> vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
>
>

Harold Hausman

5/3/2007 8:06:00 AM

0

On 5/3/07, Brad Phelan <phelan@tttech.ttt> wrote:
> Just curious,
>
> is anybody working on a C language DSL that could generate C code. I'm
> kinda interested in how it may be possible to use RSpec to unit test C
> code in a nice way.
>

I just read an interview that contained some of your keywords there:
"... I'm actually working on a tool right now I call cuby. It's a
dialect of ruby that generates C code directly."

Here's the link:
http://on-ruby.blogspot.com/2006/12/rubinius-inte...

It appears to be all jumbled up in a re-implementation of Ruby, but
might be interesting for you to check out.

hth,
-Harold

John Joyce

5/3/2007 8:17:00 AM

0


On May 3, 2007, at 4:18 PM, SonOfLilit wrote:

> Am I the only one that thinks OP is looking for a library to assist in
> generating ascii C code, like Markaby does for HTML, and not for
> executing C code that you wrote as a string?
>
> Brad, I don't think you'll find one, and in fact, I don't think you'll
> need one. Why? C has so much syntax that you're better off generating
> it with string manipulation than with a DSL.
>
> HTML is so simple that there was more gain (succintness,
> automatability) than loss (new language to learn) in Markaby and it's
> neighbours. With C, I don't see such gains overcoming the loss.
>
> Aur
>
Indeed, the OP is trying to generate C rather than write C. Very
understandable. Certainly doable. It just means a lot less of the
flexibility that C usually has (for good and bad).
But actually it is very possible to generate corresponding looping
structures, structs, functions and more.
Need to generate declarations of many variables.
To get started, find a C file that follows the conventions you want
to have generated. Rome was not built in a day and no generator will
be either.
In theory all languages end up the same in ASM anyway.
The main problem is that it must be a DSL because some Ruby
mechanisms wouldn't translate so smoothly to C.
They'd be more like Objective-C.
That said, there's a lot of typing that could be saved.
Perhaps the most painful things would be dealing with pointers and
memory management.

Paul Brannan

5/3/2007 2:23:00 PM

0

I wrote a tool a while back I called rubypp (pp as in preprocessor),
which lets you do something like this:

#include <iostream>

#ruby <<END
puts '#define FOO BAR'
'#define BAR BAZ'
END

#ruby def foo(x) ; x.to_s.chop ; end

extern "C" {
int foo(int a) {
std::cout << "1" << std::endl;
}

int foo(double a) {
std::cout << "2" << std::endl;
}

main() {
foo(1);
foo(1.0);
std::cout << "#{foo(1.0)}" << std::endl;
}

which produces this output:

#include <iostream>

#define FOO BAR
#define BAR BAZ


extern "C" {
int foo(int a) {
std::cout << "1" << std::endl;
}

int foo(double a) {
std::cout << "2" << std::endl;
}

main() {
foo(1);
foo(1.0);
std::cout << "1." << std::endl;
}

The syntax is a little odd, but it's surprisingly powerful. I use it to
generate code for nodewrap. You can find it at:

http://rubystuff.org/rubypp...

Paul


Joel VanderWerf

5/3/2007 4:03:00 PM

0

Paul Brannan wrote:
> I wrote a tool a while back I called rubypp (pp as in preprocessor),
> which lets you do something like this:
...
> #ruby def foo(x) ; > x.to_s.chop ; > end

How is this def being used in the output?

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Paul Brannan

5/3/2007 4:36:00 PM

0

On Fri, May 04, 2007 at 01:03:20AM +0900, Joel VanderWerf wrote:
> Paul Brannan wrote:
> >I wrote a tool a while back I called rubypp (pp as in preprocessor),
> >which lets you do something like this:
> ...
> >#ruby def foo(x) ; > > x.to_s.chop ; > > end
>
> How is this def being used in the output?

I wish I had a more realisitc example; I think then it would be clearer.

std::cout << "#{foo(1.0)}" << std::endl;

std::cout << "1." << std::endl;

Paul


Joel VanderWerf

5/3/2007 7:19:00 PM

0

Paul Brannan wrote:
> On Fri, May 04, 2007 at 01:03:20AM +0900, Joel VanderWerf wrote:
>> Paul Brannan wrote:
>>> I wrote a tool a while back I called rubypp (pp as in preprocessor),
>>> which lets you do something like this:
>> ...
>>> #ruby def foo(x) ; >>> x.to_s.chop ; >>> end
>> How is this def being used in the output?
>
> I wish I had a more realisitc example; I think then it would be clearer.
>
> std::cout << "#{foo(1.0)}" << std::endl;
>
> std::cout << "1." << std::endl;

My bad. I just missed that. So the #ruby stuff is for defining utility
functions that can be used inside the C code templates?

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Paul Brannan

5/4/2007 2:16:00 AM

0

On Fri, May 04, 2007 at 04:18:48AM +0900, Joel VanderWerf wrote:
> My bad. I just missed that. So the #ruby stuff is for defining utility
> functions that can be used inside the C code templates?

You can use them that way, or if the code returns non-nil, the result
will be converted to a string and inserted in the output stream, or
anything sent to stdout will also be included in the preprocessed
output.

Paul