1. Class programming with Euphoria

Wouldn't it be nice to use Phix-like class programming with Euphoria ?

Let's take this example code (test_classes_orig.oex):

include std/datetime.e 
include std/console.e 
 
function get_years(atom secs) 
  atom days = floor(secs/86400) 
  return floor(days/365.25) 
end function 
 
class person 
  datetime birth_date 
  sequence birth_place 
  sequence mother_tongue 
  procedure describe(sequence params) 
    atom secs = diff(self.birth_date, now()) 
    printf(1, "\nHello! I speak %s.\n", {self.mother_tongue}) 
    if length(params) then 
      printf(1, "I am %d years old. I was born in %s (%s).\n", {get_years(secs), self.birth_place, params[1]}) 
    else 
      printf(1, "I am %d years old. I was born in %s.\n", {get_years(secs), self.birth_place}) 
    end if 
  end procedure 
end class 
 
sequence englishman = new(person) 
englishman.birth_date = datetime:new(2010, 5, 14) 
englishman.birth_place = "Liverpool" 
englishman.mother_tongue = "english" 
 
sequence frenchman = new(person) 
frenchman.birth_date = datetime:new(1960, 8, 10) 
frenchman.birth_place = "Thionville" 
frenchman.mother_tongue = "french" 
 
sequence german = new(person) 
german.birth_date = datetime:new(1993, 2, 23) 
german.birth_place = "Munich" 
german.mother_tongue = "german" 
 
englishman.describe({"Merseyside"}) 
frenchman.describe({"Lorraine"}) 
german.describe({"Bavaria"}) 
 
maybe_any_key() 

Jean-Marc

new topic     » topic index » view message » categorize

2. Re: Class programming with Euphoria

Following preprocessor (preprocessor.ex) converts this class-oriented code to Euphoria code (basic_out.ex):

include std/io.e 
include std/text.e 
include std/regex.e 
include std/search.e 
include std/sequence.e 
include std/datetime.e 
 
constant ATTR=1, PROC=2, FUNC=3 
 
function replace_all(sequence s, sequence old, sequence new)  
  integer lg = length(old)  
  integer index = match(old, s, 1)  
  while index != 0 do  
    s = replace(s, new, index, index+lg-1)  
    index = match(old, s, index+lg)  
  end while  
  return s  
end function  
 
function manage_routine(sequence code, sequence lines, integer start) 
  integer from, upto 
   
  for i = start+1 to length(lines) do 
    if begins("end ", trim(lines[i])) then 
      code &= lines[i] & "\n" 
      start = i 
      exit 
    end if 
    sequence rule = "self\\.([_A-Za-z][_A-Za-z0-9]*)" 
    -- puts(f_out, rule & "\n") 
    regex r = regex:new(rule) 
    object found = regex:find_all(r, lines[i]) 
    if sequence(found) then 
      for j = 1 to length(found) do 
        {from, upto} = found[j][2] 
        sequence name = lines[i][from..upto] 
        {from, upto} = found[j][1] 
        lines[i] = replace(lines[i], sprintf("entity[%s]", {upper(name)}), from, upto) 
      end for 
      code &= lines[i] & "\n" 
    end if 
  end for 
  return {code, start} 
end function 

To be continued ...

</eucode>

new topic     » goto parent     » topic index » view message » categorize

3. Re: Class programming with Euphoria

procedure convertOOP(sequence inputFile, sequence outputFile) 
  sequence lines = read_lines(inputFile)  
  integer f_out = open(outputFile, "w")  
  integer from, upto 
  object found, found2 
  sequence class_name = "", name, params, elements = {} 
  integer start = 0 
   
  sequence code = "" 
   
  -- find class definition 
  regex r = regex:new("class (.*)$") 
  for i = 1 to length(lines) do 
    found = regex:find(r, lines[i]) 
    if sequence(found) then 
      {from, upto} = found[2] 
      class_name = lines[i][from..upto] 
      start = i 
      exit 
    else 
      code &= lines[i] & "\n" 
    end if 
  end for 
   
  -- add target sequence attributes 
  code &= "enum type %ENUM_TYPE% %ENUM_ITEMS% end type\n\n" 
  code &= "sequence %CLASS_NAME% = {%CLASS_CONTENT%}\n\n" 
   
  -- analyze class definition 
  sequence enums = {} 
  sequence class_definition = {} 
  integer i = start+1 
  while i <= length(lines) do 
    sequence trimmed = trim(lines[i]) 
    if match("end class", lines[i]) then 
      start = i 
      exit 
    end if 
    if begins("integer", trimmed) then 
      r = regex:new("integer (.*)$") 
      class_definition= append(class_definition, "0") 
      found = regex:find(r, lines[i]) 
      {from, upto} = found[2] 
      name = lines[i][from..upto] 
      enums = append(enums, upper(name)) 
      --code &= lines[i] & "\n" 
      elements = append(elements, {name, ATTR}) 
    elsif begins("atom", trimmed) then 
      r = regex:new("atom (.*)$") 
      class_definition= append(class_definition, "0.0") 
      found = regex:find(r, lines[i]) 
      {from, upto} = found[2] 
      name = lines[i][from..upto] 
      enums = append(enums, upper(name)) 
      --code &= lines[i] & "\n" 
      elements = append(elements, {name, ATTR}) 
    elsif begins("sequence", trimmed) then 
      r = regex:new("sequence (.*)$") 
      class_definition= append(class_definition, "\"\"") 
      found = regex:find(r, lines[i]) 
      {from, upto} = found[2] 
      name = lines[i][from..upto] 
      enums = append(enums, upper(name)) 
      --code &= lines[i] & "\n" 
      elements = append(elements, {name, ATTR}) 
    elsif begins("datetime", trimmed) then 
      r = regex:new("datetime (.*)$") 
      class_definition= append(class_definition, "now()") 
      found = regex:find(r, lines[i]) 
      {from, upto} = found[2] 
      name = lines[i][from..upto] 
      enums = append(enums, upper(name)) 
      --code &= lines[i] & "\n" 
      elements = append(elements, {name, ATTR}) 
    elsif begins("procedure", trimmed) then 
      r = regex:new("procedure (.*)\\((.*)\\)$") 
      found = regex:find(r, lines[i]) 
      {from, upto} = found[2] 
      name = lines[i][from..upto] 
      enums = append(enums, upper(name)) 
      {from, upto} = found[3] 
      params = lines[i][from..upto] 
      code &= sprintf("procedure %s(sequence entity, sequence params)\n", {name}) 
      {code, i} = manage_routine(code, lines, i) 
      elements = append(elements, {name, PROC}) 
    elsif begins("function", trimmed) then 
      r = regex:new("function (.*)\\((.*)\\)$") 
      found = regex:find(r, lines[i]) 
      {from, upto} = found[2] 
      name = lines[i][from..upto] 
      enums = append(enums, upper(name)) 
      {from, upto} = found[3] 
      params = lines[i][from..upto] 
      code &= sprintf("function %s(sequence entity, sequence params)\n", {name}) 
      {code, i} = manage_routine(code, lines, i) 
      elements = append(elements, {name, FUNC}) 
    elsif begins("end procedure", trimmed) then 
      class_definition= append(class_definition, "0") 
      code &= lines[i] & "\n" 
      code &= sprintf("%s[%s] = routine_id(\"%s\")\n", {class_name, upper(name), name}) 
    elsif begins("end function", trimmed) then 
      class_definition= append(class_definition, "0") 
      code &= lines[i] & "\n" 
      code &= sprintf("%s[%s] = routine_id(\"%s\")\n", {class_name, upper(name), name}) 
    else 
      puts(2, "Error: " & "Unexpected variable type!" & "\n")  
      abort(1)  
    end if 
    i += 1 
  end while 

To be continued ...

new topic     » goto parent     » topic index » view message » categorize

4. Re: Class programming with Euphoria

  -- replace target sequence attributes 
  code = replace_all(code, "%ENUM_TYPE%", upper(class_name)) 
  code = replace_all(code, "%ENUM_ITEMS%", join(enums, ',')) 
  code = replace_all(code, "%CLASS_NAME%", class_name) 
  code = replace_all(code, "%CLASS_CONTENT%", join(class_definition, ',')) 
   
  -- add converted code 
  puts(f_out, code) 
   
  -- add internal functions 
  puts(f_out, "\n") 
  puts(f_out, "procedure class_proc(sequence entity, integer method, sequence params = {})\n") 
  puts(f_out, "  call_proc(entity[method], {entity, params})\n") 
  puts(f_out, "end procedure\n") 
  puts(f_out, "\n") 
   
  puts(f_out, "function class_func(sequence entity, integer method, sequence params = {})\n") 
  puts(f_out, "  return call_func(entity[method], {entity, params})\n") 
  puts(f_out, "end function\n") 
  puts(f_out, "\n") 
   
  -- manage class entities 
  sequence entity_name = "" 
  sequence entities = "" 
  for l = start+1 to length(lines) do 
    -- lines[l] = trim(lines[l]) 
    sequence rule = sprintf("sequence (.*) = new\\(%s\\)$", {class_name}) 
    -- puts(f_out, rule & "\n") 
    r = regex:new(rule) 
    found = regex:find(r, lines[l]) 
    if sequence(found) then 
      {from, upto} = found[2] 
      entity_name = lines[l][from..upto] 
      entities = append(entities, entity_name) 
      printf(f_out, "sequence %s = %s\n", {entity_name, class_name}) 
    else 
      r = regex:new("([_A-Za-z0-9]*)\\.([_A-Za-z0-9]*)") 
      found = regex:find(r, lines[l]) 
      if sequence(found) then 
        {from, upto} = found[2] 
        entity_name = lines[l][from..upto] 
        if find(entity_name, entities) then  -- class referenced 
          -- check if it refers to a routine 
          {from, upto} = found[3] 
          name = lines[l][from..upto] 
          {from, upto} = found[1] 
          integer p = vlookup(name, elements, 1, 2, 0) 
          switch p do 
            case ATTR then 
              lines[l] = replace(lines[l], sprintf("%s[%s]", {entity_name, upper(name)}), from, upto) 
            case PROC then 
              from = find('(', lines[l], upto+1) 
              upto = find(')', lines[l], upto+1) 
              if upto > from+1 then 
                params = lines[l][from+1..upto-1] 
              else 
                params = {} 
              end if 
              lines[l] = sprintf("class_proc(%s, %s, %s)\n", {entity_name, upper(name), params}) 
            case FUNC then 
              from = find('(', lines[l], upto+1) 
              upto = find(')', lines[l], upto+1) 
              if upto > from+1 then 
                params = lines[l][from+1..upto-1] 
              else 
                params = {} 
              end if 
              lines[l] = sprintf("class_proc(%s, %s, %s)\n", {entity_name, upper(name), params}) 
            case else 
          end switch 
        end if 
      end if 
      puts(f_out, lines[l] & "\n") 
    end if 
  end for 
   
  close(f_out) 
end procedure 

It is launched this way:

convertOOP("test_classes_orig.oex", "basic_out.ex") 
new topic     » goto parent     » topic index » view message » categorize

5. Re: Class programming with Euphoria

Here is the resulting code:

include std/datetime.e 
include std/console.e 
 
function get_years(atom secs) 
  atom days = floor(secs/86400) 
  return floor(days/365.25) 
end function 
 
enum type PERSON BIRTH_DATE,BIRTH_PLACE,MOTHER_TONGUE,DESCRIBE end type 
 
sequence person = {now(),"","",0} 
 
datetime birth_date 
sequence birth_place 
sequence mother_tongue 
procedure describe(sequence entity, sequence params) 
atom secs = diff(entity[BIRTH_DATE], now()) 
printf(1, "\nHello! I speak %s.\n", {entity[MOTHER_TONGUE]}) 
if length(params) then 
printf(1, "I am %d years old. I was born in %s (%s).\n", {get_years(secs), entity[BIRTH_PLACE], params[1]}) 
else 
printf(1, "I am %d years old. I was born in %s.\n", {get_years(secs), entity[BIRTH_PLACE]}) 
end if 
end procedure 
person[DESCRIBE] = routine_id("describe") 
 
procedure class_proc(sequence entity, integer method, sequence params = {}) 
  call_proc(entity[method], {entity, params}) 
end procedure 
 
function class_func(sequence entity, integer method, sequence params = {}) 
  return call_func(entity[method], {entity, params}) 
end function 
 
 
sequence englishman = person 
englishman[BIRTH_DATE] = datetime:new(2010, 5, 14) 
englishman[BIRTH_PLACE] = "Liverpool" 
englishman[MOTHER_TONGUE] = "english" 
 
sequence frenchman = person 
frenchman[BIRTH_DATE] = datetime:new(1960, 8, 10) 
frenchman[BIRTH_PLACE] = "Thionville" 
frenchman[MOTHER_TONGUE] = "french" 
 
sequence german = person 
german[BIRTH_DATE] = datetime:new(1993, 2, 23) 
german[BIRTH_PLACE] = "Munich" 
german[MOTHER_TONGUE] = "german" 
 
class_proc(englishman, DESCRIBE, {"Merseyside"}) 
 
class_proc(frenchman, DESCRIBE, {"Lorraine"}) 
 
class_proc(german, DESCRIBE, {"Bavaria"}) 
 
 
maybe_any_key() 

Here is the generated output of the converted code:

 
Hello! I speak english. 
I am 11 years old. I was born in Liverpool (Merseyside). 
 
Hello! I speak french. 
I am 61 years old. I was born in Thionville (Lorraine). 
 
Hello! I speak german. 
I am 28 years old. I was born in Munich (Bavaria). 
Press Any Key to continue... 

The preprocessor can be adapted to Euphoria 3.1. Enjoy!

Jean-Marc

new topic     » goto parent     » topic index » view message » categorize

6. Re: Class programming with Euphoria

This is pretty neat. Great work! You should also check out Matt's OOEU. Basically, he's doing all the heavy lifting in the frontend and leaving the backend "clean" to output or execute the original IL. I'd like to work his changes into Euphoria at some point.

-Greg

new topic     » goto parent     » topic index » view message » categorize

7. Re: Class programming with Euphoria

Thank you Greg,

I already did so. I ported OOEU to Raspberry PI using OEU 4 code. Maybe it can help.

http://jean-marc.duro.pagesperso-orange.fr/

Jean-Marc

new topic     » goto parent     » topic index » view message » categorize

8. Re: Class programming with Euphoria

I tried this out, without, at first, any success. My attempt to diagnose the problem led me to believe that the regular expression used to identify entities was not working as the storage sequence 'entities' was always empty.

By simplifying the expression I was able to get a working version, albeit with a few extra line-feeds which I couldn't understand.

I enjoyed working through your code as it gave me added insight into a few very useful programming approaches which I can see myself using in the future.

Thank you.

new topic     » goto parent     » topic index » view message » categorize

9. Re: Class programming with Euphoria

Thank you for testing! When comparing the last working code with the published one, I saw I published an intermediate version.

Sorry for that. I have corrected the published articles after retesting.

Regards

Jean-Marc

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu