Re: Architectural Discussion: Implementing ECS and Data-Oriented Programming (DOP)

new topic     » goto parent     » topic index » view thread      » older message » newer message

I found the last running version. Here it is, complete with no additional library.

The pre-processor, classes.ex:

include std/io.e 
include std/text.e 
include std/regex.e 
include std/search.e 
include std/sequence.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 get_next_non_white(sequence code, integer from) 
  -- skip white 
  while (code[from] = ' ') or (code[from] = '\t') do 
    from += 1 
    if from > length(code) then return 0 end if 
  end while 
  return from 
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]*)" 
    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 
 
------------------------------------------------------------------------------ 
 
sequence cmds = command_line() 
sequence inFileName, outFileName 
 
for i = 3 to length(cmds) do 
  switch cmds[i] do 
    case "-i" then 
      inFileName = cmds[i+1] 
    case "-o" then 
      outFileName = cmds[i+1] 
  end switch 
end for 
 
integer f_out = open(outFileName, "w") 
sequence lines = read_lines(inFileName) 
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)) 
    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)) 
    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)) 
    elements = append(elements, {name, ATTR}) 
  elsif begins("object", trimmed) then 
    r = regex:new("object (.*)$") 
    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)) 
    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)) 
    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 
    printf(2, "Unexpected variable type at line %d!: \n", {i, lines[i]}) 
    abort(1) 
  end if 
  i += 1 
end while 
 
-- 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 
  sequence rule = sprintf("sequence (.*) = new\\(%s\\)$", {class_name}) 
  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) 

The OOP-style test program, test_classes.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() 

The shell to run the program with the pre-processor, runme.bat (Linux):

eui -p oex:classes.ex test_classes.oex 

The result:

duro@hp-zbook15g2:~/Data/Euphoria/v4/myOOP$ ./runme.bat test_classes.oex  
 
Hello! I speak english. 
I am 16 years old. I was born in Liverpool (Merseyside). 
 
Hello! I speak french. 
I am 66 years old. I was born in Thionville (Lorraine). 
 
Hello! I speak german. 
I am 33 years old. I was born in Munich (Bavaria). 

The resulting converted program, test_classes.pp.oex, used in the background:

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} 
 
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() 

Jean-Marc

new topic     » goto parent     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu