1. Conditional includes
- Posted by irv Nov 12, 2010
- 1360 views
How would I do a conditional include?
If x then include foo.e end if
Or, somewhat related question: how can I include a file specified as a string:
sequence s = "myfile.e" include s
Being able to do one (or both) of these would be a great help.
Note: the second seems like it 'should' work -
include "myfile.e" -- this does
2. Re: Conditional includes
- Posted by jimcbrown (admin) Nov 12, 2010
- 1315 views
How would I do a conditional include?
If x then include foo.e end if
Or, somewhat related question: how can I include a file specified as a string:
sequence s = "myfile.e" include s
Being able to do one (or both) of these would be a great help.
Note: the second seems like it 'should' work -
include "myfile.e" -- this does
Right now, only ifdef is supported.
ifdef LINUX then include l.e elsifdef FREEBSD then include f.e end ifdef
However, you may be able to implement better functionality through the new preprocessor interface.
3. Re: Conditional includes
- Posted by jeremy (admin) Nov 12, 2010
- 1354 views
How would I do a conditional include?
If x then include foo.e end if
What is "x" at this point?
Or, somewhat related question: how can I include a file specified as a string:
sequence s = "myfile.e" include s
Being able to do one (or both) of these would be a great help.
You cannot really do that at this point because Euphoria parses, Converts to IL, then runs. The above script would require Euphoria to be able to know what "s" is and that "s" has not changed before include s is executed. In the above situation, that is very possible but no so in the more complex situation that you would want to use this in. For example:
sequence incName = prompt_string("What file do you want to include?") include incName
This would require that the application be up and running in the interpreter before the parser ever hit line #2. See what I mean?
Now, depending on what you want to accomplish you can use the ifdef system. It controls things at parse time, but in doing so, it's not as flexible because you cannot run Euphoria scripts in order to make the decision. Let's say you have 1 of 3 database drivers you could possibly want to use:
ifdef MYSQL then include mysql.e as db elsifdef PGSQL then include pgsql.e as db elsifdef EDS then include eds.e as db elsedef crash("Valid database driver could not be determined") end ifdef
Then:
$ eui -dPGSQL myprog.ex
You can also add to your eu.cfg file the -d line.
See: man:lang_branch.html#ifdefstatement
Jeremy
4. Re: Conditional includes
- Posted by DerekParnell (admin) Nov 12, 2010
- 1341 views
How would I do a conditional include?
If x then include foo.e end if
Files are included during the parsing phase and not at execution phase. The "if" statement is a runtime thing so by the time the "if" is executed, all the included files are already included.
However, to have a file optionally included during parsing, you can use the "ifdef" statement, which is a parse-time thing.
ifdef x then include foo.e end ifdef
Also note that files can only be included at the 'top level', which means that you cannot include a file inside a function, for example.
Eu4 uses the "ifdef" a great deal to include things that are only relevant to Unix or to Windows systems as appropriate.
Or, somewhat related question: how can I include a file specified as a string:
sequence s = "myfile.e" include s
Being able to do one (or both) of these would be a great help.
Note: the second seems like it 'should' work -
include "myfile.e" -- this does
You can't. The syntax ...
include "myfile.e" -- this does
is used when you have spaces embedded in the included file's path. For example...
include "C:/program files/Euphoria Utilities/myfile.e"
Maybe we can better help you if you explain what it is you are trying to do rather telling us how you want to do it.
5. Re: Conditional includes
- Posted by irv Nov 12, 2010
- 1332 views
OK, here's an abbreviated example:
-- Main.ex include std/dll.e -- user-written files; public include file.e -- handler library, included with every program; include funks.e printf(1,"Main sez routine_id is: %d\n",{routine_id("file:close")})
-- file.e namespace file -- This file would be written by the user, and 'included' in main.ex global function close() -- it's global? puts(1,"File closed\n") return 1 end function printf(1,"File sez routine_id is %d\n",{routine_id("close")})
-- funks.e -- This file is fixed, non-editable. It cannot 'see' the user-written includes, -- but it must access the user-written functions in those includes. export procedure do_fn(sequence s) atom rid = routine_id(s) printf(1,"do_fn sez routine_id is: %d\n",{rid}) --call_func(rid) end procedure -- code here decides which routine will be called. -- based on which button pushed, etc. -- if btn.name = "file_close" then do_fn("file:close") -- as an example
In real life, of course, funks.e would be a much larger file, one which the user would have no business editing.
Apparently, global and public mean something different to programmers than to Webster. Do we need new scope words like Universal and CommonKnowledge?
At any rate - that's what I'm trying to do. IF we could do that (I think it's impossible in Euphoria) it would make for much cleaner, easier to use code (The Glade IDE could, for example, be used to create an entire Eu program)
6. Re: Conditional includes
- Posted by jimcbrown (admin) Nov 12, 2010
- 1324 views
IF we could do that (I think it's impossible in Euphoria) it would make for much cleaner, easier to use code (The Glade IDE could, for example, be used to create an entire Eu program)
You're right, it is impossible in Euphoria.
However, you can use a preprocessor to work around this. Replace your call to routine_id() with get_rid() in funks.e as so...
export procedure do_fn(sequence s) atom rid = get_rid(s) printf(1,"do_fn sez routine_id is: %d\n",{rid}) --call_func(rid) end procedure
Then call main.ex like this:
eui -p e,ex:rid.ex main.ex
You'll need the following file in your include path somewhere, it's a helper library that the preprocessor, rid.ex, rewrites your program to use.
Name it save_rid.e
sequence allrids = "", allnames = "" public procedure save_rid(integer rid, sequence name) allrids = append(allrids, rid) allnames = append(allnames, name) end procedure public function get_rid(sequence name) integer i i = find(name, allnames) if i then return allrids[i] else return -1 end if end function
Now here's the output when I run main.ex:
File sez routine_id is 446 do_fn sez routine_id is: 446 Main sez routine_id is: 446
Here is rid.ex
--JBrown --combine.ex --global scopes aren't redefined --this MIGHT cause an error, however, euphoria should normally have an --error in that case too. --procedure-scope vars aren't saved, this makes code more readable --N/A if shroud is on --you can either choose not to combine files in a list, or --you can ignore include files on 1 dir (default: eu's include dir) --shrouding symbols is optional. --bug fixed: local symbols were being treated as global. --bug fixed: strings with \x were changed to \x\x. --bug fixed: file c:\myfile.e and myfile.e were the diffrent files. --added platform to sequence builtin --bug: using routine_id("abort_") gives error --"Unable to resolve routine id for abort" --to fix made routine_id-ing of builtin routines legal. -- concat.ex -- version 1.0 -- -- replacement for Euphoria's bind routine include std/cmdline.e include std/get.e include std/io.e include std/map.e include std/text.e include std/console.e include std/sequence.e include std/filesys.e integer slash if platform() = 3 then slash = '/' else slash = '\\' end if ----------------------------------------------------------------------------- -- code from RDS's ED color coding routines sequence charClass -- character classes constant DIGIT = 1, OTHER = 2, LETTER = 3, BRACKET = 4, QUOTE = 5, DASH = 6, WHITE_SPACE = 7, NEW_LINE = 8 charClass = repeat( OTHER, 255 ) charClass['a'..'z'] = LETTER charClass['A'..'Z'] = LETTER charClass['_'] = LETTER charClass['0'..'9'] = DIGIT charClass['['] = BRACKET charClass[']'] = BRACKET charClass['('] = BRACKET charClass[')'] = BRACKET charClass['{'] = BRACKET charClass['}'] = BRACKET charClass['\''] = QUOTE charClass['"'] = QUOTE charClass[' '] = WHITE_SPACE charClass['\t'] = WHITE_SPACE charClass['\n'] = NEW_LINE charClass['-'] = DASH --**** -- == Keyword Data include euphoria/keywords.e ----------------------------------------------------------------------------- integer globalState, -- 1 = just set, 2 = old flag routineFlag, -- 1 = last keyword was routine_id procState, -- 1 = keyword follows, 2 = inProc outFile -- file to write to globalState = 0 routineFlag = 0 procState = 0 sequence oldLocal, newLocal, included oldLocal = {} newLocal = {} included = {} constant EuPlace = getenv( "EUDIR" ), --Place = { "", EuPlace & "\\", EuPlace & "\\INCLUDE\\" } Place = { current_dir()&slash, EuPlace & slash, EuPlace & slash&"include"&slash, "" } ----------------------------------------------------------------------------- function findFile( sequence fName ) -- returns where a file is -- looks in the usual places -- look in the usual places --trace(1) if find(fName[length(fName)], {10, 13}) then fName = fName[1..length(fName)-1] end if for i = 1 to length( Place ) do if sequence( dir( Place[i] & fName ) ) then if platform() = 3 then return Place[i] & fName else return upper( Place[i] & fName ) end if end if end for printf( 1, "Unable to locate file %s.\n", {fName} ) --abort(0) end function ----------------------------------------------------------------------------- function replaceWord( sequence word ) -- replace obsolete builtin functions with valid names, so -- the resulting program does not crash with unresolved symbols return word end function ----------------------------------------------------------------------------- sequence funcName = "", nameSpace = "" integer inNameSpace = 0 function parseLine( sequence s ) -- parse a sequence into keywords -- and convert them integer at, char, i sequence out, word out = {} at = 1 while at <= length( s ) do -- get a character char = s[at] -- identifier if charClass[char] = LETTER then word = {} -- read until end while charClass[char] = LETTER or charClass[char] = DIGIT do -- add to word word = append( word, char ) -- next letter at += 1 char = s[at] end while -- routine flag routineFlag = equal( word, "routine_id" ) -- substitute? word = replaceWord( word ) -- global flag if equal( word, "global" ) then -- new occurance globalState = 1 elsif globalState = 1 then -- mark as used globalState = 2 end if if inNameSpace = 1 then inNameSpace = 2 nameSpace = word end if if equal( word, "namespace" ) then inNameSpace = 1 end if -- manage proc state if equal( word, "function" ) or equal( word, "procedure" ) or equal( word, "type" ) then if procState = 0 then -- beginning of definition procState = 1 elsif procState = 2 then -- end function/procedure procState = 0 word = word & sprintf(" save_rid(routine_id(\"%s\"), \"%s:%s\")", {funcName, nameSpace, funcName}) end if elsif procState = 1 then -- move state ahead procState = 2 globalState = 0 funcName = word end if -- substitute, if needed out = out & word -- number: handles hex as well elsif charClass[char] = DIGIT or char = '#' then word = {} -- read until end while charClass[char] = DIGIT or charClass[char] = LETTER or char = '#' do -- add to word word = append( word, char ) -- next letter at += 1 char = s[at] end while -- accumulated number out = out & word -- comment elsif char = '-' and s[at+1] = '-' then -- comment out = out & s[at..length(s)] -- move past end at = length(s)+1 -- character literal elsif char = '\'' then at += 1 if s[at] = '\\' then -- special at += 1 word = "'\\" & s[at] & "'" else -- normal word = "'" & s[at] & "'" end if -- move past quote at += 2 -- accumulate out = out & word -- quote elsif char = '"' then word = {'"'} while 1 do -- move ahead at += 1 if at > length(s) then exit end if -- special? if at <= length(s) and s[at] = '\\' then at += 1 --word = word & s[at-1] & s[at] word = word & '\\' & s[at] -- prevent reading as quote s[at] = ' ' elsif at <= length(s) then word = word & s[at] end if -- end of quote? if at <= length(s) and s[at] = '"' then -- move ahead and exit at += 1 exit end if end while -- handle routine_id if routineFlag then -- remove quotes word = word[2..length(word)-1] word = replaceWord(word) -- re-apply quotes word = '"' & word & '"' end if -- accumulated out = out & word -- delimiter else out = out & char at += 1 end if end while if inNameSpace = 2 then inNameSpace = 0 out = {out, "include save_rid.e\n" } end if return out end function ----------------------------------------------------------------------------- function getIncludeName( sequence data ) -- if the statement is an include statement, return the file name integer at -- include statement missing? if not match( "include ", data ) then return "" end if -- trim white space while charClass[ data[1] ] = WHITE_SPACE do data = data[2..length( data ) ] end while -- line feed? if find( '\n', data ) then data = data[1..length(data)-1] end if if find( '\r', data ) then data = data[1..length(data)-1] end if -- not first statement? if not equal( data[1..8], "include " ) then -- not an include statement return "" else -- remove statement data = data[9..length(data)] end if -- remove data after space at = find( ' ', data ) if at then data = data[1..at-1] end if return data end function ----------------------------------------------------------------------------- function trimer(sequence s) sequence t integer u if s[length(s)] = '\n' then s = s[1..length(s)-1] end if if s[length(s)] = '\r' then s = s[1..length(s)-1] end if t = reverse(s) u = find(slash, t) if not u then return s end if t = t[1..u-1] s = reverse(t) return s end function function includable(sequence name) return 0 end function ----------------------------------------------------------------------------- without warning procedure parseFile( sequence fName ) integer inFile sequence newPrior, oldPrior, includeName, oldNameSpace = nameSpace object data, seqall seqall = "" nameSpace = fName -- find the file fName = findFile( fName ) -- already part of the file? if find( fName, included ) then nameSpace = oldNameSpace return else included = append( included, fName ) end if -- store locals and clear oldPrior = oldLocal newPrior = newLocal oldLocal = {} newLocal = {} -- write header puts( outFile, "\n" ) inFile = open( fName, "r" ) while 1 do -- read a line data = gets( inFile ) -- end of file? if integer( data ) then exit end if -- include file? includeName = getIncludeName( data ) if length( includeName ) and includable(trimer(includeName)) then -- include the file parseFile( includeName ) elsif length(includeName) then --no parse --puts( outFile, data ) seqall = append(seqall, data) else -- translate data = parseLine( data ) -- output --puts( outFile, data ) if length(data) and sequence(data[1]) then seqall &= data else seqall = append(seqall, data) end if end if end while close( inFile ) --end of file header integer gotit = 0 for i = 1 to length(seqall) do if equal(seqall[i], "include save_rid.e\n") then gotit = 1 end if end for if not gotit then seqall = { "include save_rid.e\n" } & seqall end if for i = 1 to length(seqall) do puts( outFile, seqall[i] ) end for puts( outFile, "\n" ) -- restore locals oldLocal = oldPrior newLocal = newPrior end procedure with warning ----------------------------------------------------------------------------- export function preprocess(sequence inFileName, sequence outFileName, sequence optParams) -- make sure they are different if equal( inFileName, outFileName ) then puts( 1, "File names must be different!\n" ) return 1 end if -- open the file outFile = open( outFileName, "w" ) -- process the input file parseFile( inFileName ) -- close the output file close( outFile ) return 0 end function ----------------------------------------------------------------------------- constant cmd_params = { { "i", 0, "Input filename", { NO_CASE, HAS_PARAMETER, ONCE, "filename" } }, { "o", 0, "Output filename", { NO_CASE, HAS_PARAMETER, ONCE, "filename" } } } procedure run() object optParams optParams = {} -- read the command line map:map params = cmd_parse(cmd_params) object inFileName=map:get(params, "i"), outFileName=map:get(params, "o") -- get input file if atom(inFileName) then inFileName = prompt_string( "File to concatonate? " ) if length( inFileName ) = 0 then abort(0) end if end if -- get output file if atom(outFileName) then outFileName = prompt_string( "File to create? " ) if length( outFileName ) = 0 then abort(0) end if end if -- make sure they are different if preprocess( inFileName, outFileName, optParams ) then abort(0) end if end procedure ifdef not EUC_DLL then run() end ifdef
7. Re: Conditional includes
- Posted by irv Nov 12, 2010
- 1283 views
Interestingly enough, it worked, even though it failed (due to insufficient privileges, I think)
/home/irv/rid.ex:435 in procedure parseFile() bad file number (-1) ... called from /home/irv/rid.ex:514 in function preprocess() ... called from /home/irv/rid.ex:555 in procedure run() ... called from /home/irv/rid.ex:562 --> See ex.err Preprocessor command failed (256): eui rid.ex -i /usr/share/euphoria/include/std/dll.e -o /usr/share/euphoria/include/std/dll.pp.e File sez routine_id is 2 do_fn sez routine_id is: 2 Main sez routine_id is: 2
8. Re: Conditional includes
- Posted by jimcbrown (admin) Nov 12, 2010
- 1269 views
Um, well, yeah... heh, heh...
rid.ex was just a quick&dirty hack to prototype a proof-of-concept.
It's impossible in Euphoria, but you can do it in a preprocessor, here's an quick example, you can redo it much better, etc...
Interestingly enough, it worked, even though it failed (due to insufficient privileges, I think)
/home/irv/rid.ex:435 in procedure parseFile() bad file number (-1) ... called from /home/irv/rid.ex:514 in function preprocess() ... called from /home/irv/rid.ex:555 in procedure run() ... called from /home/irv/rid.ex:562 --> See ex.err Preprocessor command failed (256): eui rid.ex -i /usr/share/euphoria/include/std/dll.e -o /usr/share/euphoria/include/std/dll.pp.e File sez routine_id is 2 do_fn sez routine_id is: 2 Main sez routine_id is: 2
9. Re: Conditional includes
- Posted by mattlewis (admin) Nov 12, 2010
- 1289 views
At any rate - that's what I'm trying to do. IF we could do that (I think it's impossible in Euphoria) it would make for much cleaner, easier to use code (The Glade IDE could, for example, be used to create an entire Eu program)
You could do something were you require the file to be a particular name, and to be in the same directory as the main file. This directory would be in the include path. So, suppose you require that the user puts their stuff in a file called gtk_custom.e, then you could:
-- funks.e -- This file is fixed, non-editable. It cannot 'see' the user-written includes, -- but it must access the user-written functions in those includes. export procedure do_fn(sequence s) atom rid = routine_id(s) printf(1,"do_fn sez routine_id is: %d\n",{rid}) --call_func(rid) end procedure -- code here decides which routine will be called. -- based on which button pushed, etc. -- if btn.name = "file_close" then do_fn("custom:close") -- as an example include gtk_custom.e as custom
Note also that default namespaces (which are declared using namespace foo at the top of a file) are public symbols, meaning that they can be transmitted via indirect includes:
-- a.e namespace a_e procedure foo() ... end procedure -- b.e public include a.e -- c.ex include b.e -- c.ex can see namespace a_e since b.e used public include, and -- c.ex includes b.e a_e:foo()
Matt