#!/usr/bin/perl5 -w

### Name: c2js

### Author: Douglas Sweetser
### sweetser@TheWorld.com

### program description
my $help_string = <<HELP;

usage: c2js [-help] program.c(s)

translates quaternion c programs to javascript given file(s)
output is firstfile.c.js

very sensitive to syntax!
must use conventions found in Qlib.c

Uses XML syntax around subroutines
/* <function name="foo"> */
/* </function> */

subroutines declarations: 
*return         on separate line
name(vars) {    at start of next

1 declaration per line
initial values defined, such as "double d = 3;"

object*
constructObject(var) {    for new object creation

HELP
 

### algorithm
#   reads in files sequentially
#   translates to javascript
#   does not write (many) unused library functions


### bugs, upgrades
#   may require much maintenance, arg!


### modules
use strict;
use English;
use Getopt::Long;
    $Getopt::Long::autoabbrev = 1;   # use 1 letter
    $Getopt::Long::ignorecase = 0;   # be case sensitive please


### variables
my($program, @programs);           # The code, array of the code
   $program = "";
my($function_name, $function_flag);
   $function_flag = 1;
my($function, @functions, %functions);  # used in the code 
my($library, $missing_functions);       # missing from javascript
   $library = "";
my($lib);                               # small library
my($file, $file_in, $file_out);
my($line);
my($help_flag);


### prototypes
sub _get_data ();           # command line processing
sub translate_c_2_js ($);   # translates c to javascript
sub mini_library ($$);      # returns only library functions used
sub _get_functions ($$);    # gets functions based on XML tags

### main
# get data from command line
_get_data();


# Translate from c to javascript library, programs
$file_in = "Qlib.c";
open(FILE_IN, $file_in) || die ("could not open $file_in: $!");
while(<FILE_IN>) {
    $library .= $_;
}
close(FILE_IN);

$library = translate_c_2_js($library);


# go through each file
for $file (@ARGV) {

    my $temp = "";
    
    # add each line to $program
    open(FILE_IN, $file) || die ("could not open $file: $!");
    while(<FILE_IN>) {
        $temp .= $_;
    }
    close(FILE_IN);

    $program .= translate_c_2_js($temp);    
}

# TESTING:{print "$program is: $program\n"};

# make a unique list of all functions used
(@functions) = $program =~ /(\w*)\(/gm;

foreach $function (@functions) {
    $functions{$function} = "";
}


# math functions in C, not found in javascript
$missing_functions = <<FUNC;

/* <function name="sinh" var="Z *Z" 
task="returns the sinh of a number: (e^z - e^-z)/2"> */

function sinh(Z) {
  return (Math.exp(Z) - Math.exp(-Z))/2;
}
/* </function> */


/* <function name="cosh" var="Z *Z" 
task="returns the cosh of a number: (e^z + e^-z)/2"> */
 
function cosh(Z) {
  return (Math.exp(Z) + Math.exp(-Z))/2;
}
/* </function> */


/* <function name="tanh" var="Z *Z" 
task="returns the sinh of a number: sinh(Z) / cosh(Z)"> */

function tanh(Z) {
    return sinh(Z) / cosh(Z);
}
/* </function> */

FUNC

# add to javascript library
$library .= $missing_functions;


# open file for output
$file_out = $ARGV[0] . ".js";
open(FILE_OUT, ">$file_out");

# process lines individually
@programs = split("\n", $program);

for $line (@programs) {

    print FILE_OUT $line, "\n";

    # at main, plug in library
    if ($line =~ /main\(\)/) {

        $lib = mini_library($library, \%functions);

	print FILE_OUT $lib;
    }
}

close(FILE_OUT);


### signals
exit(0);


### subroutines

# Get data from the command line
#
sub _get_data () {
    GetOptions("help" => \$help_flag);

    # print the help message
    if ($help_flag) {
        print ("$help_string");
        exit(0);
    }
}


# Given:
#   a library as a string
#   a pointer to a hash of used functions
# Returns a string of only those functions used
# NOTE: requires XML tags
#
sub mini_library ($$) {

    ### algorithm
    #   calls _get_functions with same arguments
    #   determines unique internal functions
    #   calls _get_functions again

    ### variables
    my ($input); 
    my ($func, @funcs, %functions_used, %functions_internal);
    my $result = "";
    my ($temp);

    # assign
    $input = $_[0];
    %functions_used = %{$_[1]};

    # include functions in the program to the result
    $result .= _get_functions ($input, \%functions_used) . "\n";

    # add internally used library functions
    # trying to avoid function foo by look for 
    # (function(,    function(, = function( and , function( 
    @funcs =   $input =~ /\((\w{2,})\(/gm;
    push @funcs,  $result =~ /^\s+(\w{2,})\(/gm;
    push @funcs,  $result =~ /\=\s+(\w{2,})\(/gm;
    push @funcs,  $result =~ /\,\s+(\w{2,})\(/gm;

    # find unique internal functions
    foreach $func (@funcs) {
        $functions_internal{$func} = "" unless (exists $functions_used{$func});
    }

    # include internal functions in the library to the result
    $result .= _get_functions ($input, \%functions_internal) . "\n";

    # return a string of code
    return $result;
}


# Given:
#   a library as a string
#   a pointer to a hash of used functions
# Returns a string of only those functions used
# NOTE: requires XML tags
#
sub _get_functions ($$) {

    ### algorithm
    #   loops through code one line at a time
    #   sets flag based on XML tag
    #   adds function if in hash passed in

    ### variables
    my($input, $library_line, @library_lines);
    my(%functions_used);
    my $result = "";

    # assign
    $input = $_[0];
    %functions_used = %{$_[1]};

    # go through lines 1 by 1
    @library_lines = split("\n", $input);
    foreach $library_line (@library_lines) {
            
        # get the function name from XML tag
        ($function_name) = $library_line =~ /function name=\"(\w*)\"/;

        # set a flag if it exists
        $function_flag = 1 if ($function_name and exists($functions_used{$function_name}));
        $function_name = "";

        #print
        $result .= $library_line . "\n" if($function_flag); 

        #reset
        if ($library_line =~ /<\/function/) {
            $function_flag = 0;
            $result .= "\n";
        }
    }

    # return a string of functions
    return $result;
}


# given a string in C
# returns a translated string
#
sub translate_c_2_js($) {

    ### algorithm
    #   regular expression replacement for language differences
    #   go through line by line for object creation code, printing

    ### variables
    my ($c, @c_lines);
    my $this = "";
    my $line = "";
    my $scope_flag = 0;

    # assign
    $c = $_[0];

    # replacements from C to javascript
    # start with most specific changes, then go to general stuff
    # stuff at start of files
    $c =~ s/\#include.+//g;      # remove include statements
    $c =~ s/\#define\s+(\w+)\s+([\w.]+)/$1 = $2\;/ig;
    $c =~ s/(void main\(\))/\/\* $1 \*\//g;  # comment out void mains

    # subroutines
    $c =~ s/^\w+\*?\s?$//mg;         # subroutine return declarations
    $c =~ s/(^\w+\()/function $1/mg; # convert subroutines into functions
 
    # declarations
    $c =~ s/Q \*\w+\;//g;
    $c =~ s/Q //g;
    $c =~ s/double //g;

    # pointers
    $c =~ s/-\>/\./g;
    $c =~ s/\\n/\<BR\>/g;
    $c =~ s/\*(\w)/$1/g;
    $c =~ s/\&(\w)/$1/g;

    # objects
    $c =~ s/construct//g;
    $c =~ s/return Q\(/return new Q\(/g;
    $c =~ s/(\w+ \= )(\w+\([\d\, \)\;]+\s?)$/var $1new $2/gm;

    #printing
    $c =~ s/\%\w*\s*//g;

    # translate C's math functions into javascript Math.methods
    $c =~ s/( |\()(abs\()/$1Math.$2/g;
    $c =~ s/( |\()(acos\()/$1Math.$2/g;
    $c =~ s/( |\()(asin\()/$1Math.$2/g;
    $c =~ s/( |\()(atan\()/$1Math.$2/g;
    $c =~ s/( |\()(atan2\()/$1Math.$2/g;
    $c =~ s/( |\()(ceil\()/$1Math.$2/g;
    $c =~ s/( |\()(cos\()/$1Math.$2/g;
    $c =~ s/( |\()(exp\()/$1Math.$2/g;
    $c =~ s/( |\()(floor\()/$1Math.$2/g;
    $c =~ s/( |\()(log\()/$1Math.$2/g;
    $c =~ s/( |\()(max\()/$1Math.$2/g;
    $c =~ s/( |\()(min\()/$1Math.$2/g;
    $c =~ s/( |\()(pow\()/$1Math.$2/g;
    $c =~ s/( |\()(random\()/$1Math.$2/g;
    $c =~ s/( |\()(round\()/$1Math.$2/g;
    $c =~ s/( |\()(sin\()/$1Math.$2/g;
    $c =~ s/( |\()(sqrt\()/$1Math.$2/g;
    $c =~ s/( |\()(tan\()/$1Math.$2/g;


    # must search out object creation, use this
    @c_lines = split("\n", $c);

    # reset
    $c = "";      

    # cycle through code one line at a time

    for $line (@c_lines) {

        # find code with malloc, set a flag
	if ($line =~ /.*malloc.*/) {
            ($this) = $line =~ /(\w+)\s+\=/;
            $line = "";
            $scope_flag = 1;
	}

        # use this for objects
        $line =~ s/\b$this\b/this/g if ($this && $scope_flag);

        # unset the flag
        $scope_flag = 0 if ($line =~ /\}/);

        # modify printf statments
        if ($line =~ /printf/) {
            $line =~ s/printf/document\.write/;

            # Buggy - how to put in spaces in document.write?
            $line =~ s/,/, '  ',/g;
	}

        $c .= $line . "\n";
    }

    # return string with translated code
    return $c;
}



### perldoc
=pod

=head1 NAME

c2js - converse quaternion c programs to javascript

=head1 SYNOPSIS

B<c2js> [-help] programs.c(s)

=head1 DESCRIPTION

Can directly convert quaternion.c programs into javascript

=head1 OPTIONS

=item I<-help>

usage: c2js [-help] program.c(s)

translates quaternion c programs to javascript given file(s)
output is firstfile.c.js

very sensitive to syntax!
must use conventions found in Qlib.c

Uses XML syntax around subroutines
/* <function name="foo"> */
/* </function> */

subroutines declarations: 
*return - on separate line
name(vars) { - at start of next

1 declaration per line
initial values defined, such as "double d = 3;"

object*
constructObject(var) {    for new object creation

=head1 AUTHOR

Doug Sweetser, sweetser@TheWorld.com

=head1 LICENSE

GPL - GNU General Public License, version 2

=cut






