1. Class programming with Euphoria
- Posted by jmduro Oct 24, 2021
- 1007 views
- Last edited Oct 26, 2021
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
2. Re: Class programming with Euphoria
- Posted by jmduro Oct 24, 2021
- 1007 views
- Last edited Oct 26, 2021
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>
3. Re: Class programming with Euphoria
- Posted by jmduro Oct 24, 2021
- 1012 views
- Last edited Oct 26, 2021
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 ...
4. Re: Class programming with Euphoria
- Posted by jmduro Oct 24, 2021
- 1010 views
- Last edited Oct 26, 2021
-- 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")
5. Re: Class programming with Euphoria
- Posted by jmduro Oct 24, 2021
- 1026 views
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
6. Re: Class programming with Euphoria
- Posted by ghaberek (admin) Oct 24, 2021
- 1005 views
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
7. Re: Class programming with Euphoria
- Posted by jmduro Oct 25, 2021
- 965 views
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
8. Re: Class programming with Euphoria
- Posted by dr_can Oct 25, 2021
- 950 views
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.
9. Re: Class programming with Euphoria
- Posted by jmduro Oct 26, 2021
- 905 views
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