1. Conditional includes

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 
new topic     » topic index » view message » categorize

2. Re: Conditional includes

irv said...

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.

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

3. Re: Conditional includes

irv said...

How would I do a conditional include?

If x then  
  include foo.e 
end if 

What is "x" at this point?

irv said...

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

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

4. Re: Conditional includes

irv said...

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.

irv said...

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.

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

5. Re: Conditional includes

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)

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

6. Re: Conditional includes

irv said...

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 
 
new topic     » goto parent     » topic index » view message » categorize

7. Re: Conditional includes

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 

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

8. Re: Conditional includes

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...

irv said...

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 

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

9. Re: Conditional includes

irv said...

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

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

Search



Quick Links

User menu

Not signed in.

Misc Menu