Re: Architectural Discussion: Implementing ECS and Data-Oriented Programming (DOP)
- Posted by jmduro 3 weeks ago
- 191 views
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

