how to to create a mulitline macro in julia? - macros

macro Estruct(name,arg,type,max=100,min=0,descritpion="")
:(struct $(esc(name))
$((esc(arg)))
$(esc(name))($(esc(arg)))=new(
check($(esc(type)),$(esc(arg)),$(esc(name)),$(esc(max)),$(esc(min)),$(esc(descritpion))))
end)
end
how can I imeplent this macro like this:
#Estruct begin
B
arg1
Float64
200.0
5.0
"this"
end
I don't know how to make a multiline macro. I thought I just have to add begin and end, but I'm always getting: MethodError: no method matching var"#Estruct"(::LineNumberNode, ::Module, ::Expr)

There's no such thing as a multiline macro, that's just a macro that takes a block as an argument. You can see how the macro gets invoked by writing a dummy version of it that just returns its arguments:
macro Estruct(args...); args; end
Now you can invoke that the way you want to call it and it will return its arguments as a tuple:
julia> args = #Estruct begin
B
arg1
Float64
200.0
5.0
"this"
end
(quote
#= REPL[12]:2 =#
B
#= REPL[12]:3 =#
arg1
#= REPL[12]:4 =#
Float64
#= REPL[12]:5 =#
200.0
#= REPL[12]:6 =#
5.0
#= REPL[12]:7 =#
"this"
end,)
julia> typeof(args)
Tuple{Expr}
julia> dump(args[1])
Expr
head: Symbol block
args: Array{Any}((12,))
1: LineNumberNode
line: Int64 2
file: Symbol REPL[12]
2: Symbol B
3: LineNumberNode
line: Int64 3
file: Symbol REPL[12]
4: Symbol arg1
5: LineNumberNode
line: Int64 4
file: Symbol REPL[12]
...
8: Float64 200.0
9: LineNumberNode
line: Int64 6
file: Symbol REPL[12]
10: Float64 5.0
11: LineNumberNode
line: Int64 7
file: Symbol REPL[12]
12: String "this"
julia> args[1].args
12-element Vector{Any}:
:(#= REPL[12]:2 =#)
:B
:(#= REPL[12]:3 =#)
:arg1
:(#= REPL[12]:4 =#)
:Float64
:(#= REPL[12]:5 =#)
200.0
:(#= REPL[12]:6 =#)
5.0
:(#= REPL[12]:7 =#)
"this"
This tells you that the macro gets called with a single argument which is an Expr with head type :block, i.e. the one argument is a quoted block. To implement your "multiline macro" you need to implement the macro body to accept a quoted block expression and process it, presumably by looking at its .args field which is where all the expressions you're interested in are. You'll probably want to ignore all the LineNumberNode objects in there and just process the other items in the block.

Related

Using numba in a method to randomize matrices

I'm still not very familiar with numba and my problem is that I have the piece of code bellow that I use for randomize the edges of graphs.
This code is simply used to swap some edges in a connectivity matrix given the number of desired swaps and a seed for the random number generator.
My problem is that when I try to use it with numba to speed it up I did not menage to run it. The error it returns is also pasted bellow.
#nb.jit(nopython=True)
def _randomize_adjacency_wei(A, n_swaps, seed):
np.random.seed(seed)
# Number of nodes
n_nodes = A.shape[0]
# Copy the adj. matrix
Arnd = A.copy()
# Choose edges that will be swaped
edges = np.random.choice(n_nodes, size=(4, n_swaps), replace=True).T
#itr = range(n_swaps)
#for it in tqdm(itr) if verbose else itr:
it = 0
for it in range(n_swaps):
i,j,k,l = edges[it,:]
if len(np.unique([i,j,k,l]))<4:
continue
else:
# Old values of weigths
w_ij,w_il,w_kj,w_kl=Arnd[i,j],Arnd[i,l],Arnd[k,j],Arnd[k,l]
# Swaping edges
Arnd[i,j]=Arnd[j,i]=w_il
Arnd[k,l]=Arnd[l,k]=w_kj
Arnd[i,l]=Arnd[l,i]=w_ij
Arnd[k,j]=Arnd[j,k]=w_kl
return Arnd
TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<function unique at 0x7f1a1c03b0d0>) found for signature:
>>> unique(list(int64)<iv=None>)
There are 2 candidate implementations:
- Of which 2 did not match due to:
Overload in function 'np_unique': File: numba/np/arrayobj.py: Line 1915.
With argument(s): '(list(int64)<iv=None>)':
Rejected as the implementation raised a specific error:
TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Unknown attribute 'ravel' of type list(int64)<iv=None>
File "../../../home/vinicius/anaconda3/lib/python3.8/site-packages/numba/np/arrayobj.py", line 1918:
def np_unique_impl(a):
b = np.sort(a.ravel())
^
During: typing of get attribute at /home/vinicius/anaconda3/lib/python3.8/site-packages/numba/np/arrayobj.py (1918)
File "../../../home/vinicius/anaconda3/lib/python3.8/site-packages/numba/np/arrayobj.py", line 1918:
def np_unique_impl(a):
b = np.sort(a.ravel())
^
raised from /home/vinicius/anaconda3/lib/python3.8/site-packages/numba/core/typeinfer.py:1071
During: resolving callee type: Function(<function unique at 0x7f1a1c03b0d0>)
During: typing of call at <ipython-input-165-90ffd30fe0e8> (19)
File "<ipython-input-165-90ffd30fe0e8>", line 19:
def _randomize_adjacency_wei(A, n_swaps, seed):
<source elided>
i,j,k,l = edges[it,:]
if len(np.unique([i,j,k,l]))<4:
^
Thanks in advance,
Vinicius
According to the comments, you are passing a list to np.unique() but this is not supported by Numba.
Modifying the code this way:
i, j, k, l = e = edges[it, :]
if len(np.unique(e)) < 4:
...
The following example doesn't produce any errors:
>>> A = np.random.randint(0, 5, (8,8))
>>> r = _randomize_adjacency_wei(A, 4, 33)

Protected execution, 2 cases

Why does in first following case protected execution work, but in second does not?:
q)t:([]a:1 2;b:3 4);
q)#[#[cols t; ; :; `bb]; (cols t)?`b; `columnNotFound]
`a`bb
q)#[#[cols t; ; :; `cc]; (cols t)?`c; `columnNotFound] // 1. works perfectly
`columnNotFound
q)#[#[cols t; (cols t)?`c; :; `cc]; `; `columnNotFound] // 2. exception does not handled
'length
[0] #[#[cols t; (cols t)?`c; :; `cc]; `; `columnNotFound]
^
Upd:
Hmm, I suspect something after trying:
q)#[{#[cols t; (cols t)?`c; :; `cc]}; `; `columnNotFound]
`columnNotFound
The protected execution is using the argument you're supplying. The first two examples are projections but the last is not, so it fails on execution.
q){#[cols t;x;:;`bb]}(cols t)?`b
`a`bb
q){#[cols t;x;:;`cc]}(cols t)?`c / thrown into error trap
'length
[1] {#[cols t;x;:;`cc]}
^
q))\
q)#[cols t;(cols t)?`c;:;`cc] / fails on execution
'length
[0] #[cols t;(cols t)?`c;:;`cc]
^
q)
In your upd, making the # apply a function is forcing the argument in the protected execution to be used.
q){#[cols t;(cols t)?`c;:;`cc]}`
'length
[1] {#[cols t;(cols t)?`c;:;`cc]}
^
q))

Attempt to call global function is nil, but function is not shown in debugger?

I am using Eclipse LDT for development, using the packaged Lua EE and Interpreter for Lua 5.2. I need to call the commandDatabase() method from my main method, though when I try, I receive the error:
"attempt to call global 'commandDatabase' (a nil value)".
I have looked up this error, and I am, as far as I can tell, defining methods in the right order.
Lua - attempt to call global 'contains' (a nil value)
When I view it in the debugger, the interpreter does not seem to find any methods I define between commandSystem and commandHelp. It shows each other function in the Variables area as e.g. ["commandSystem"] = function() but commandDatabase() does not appear
I have tried calling a wrapper method like so:
function commandDatabaseStep()
return commandDatabase()
end
... but this did not work either (same error)
The relevant code:
-- System command
function commandSystem()
...
end
-- Database command
function commandDatabase()
if arguments[2] == "no_arg1" then
print("The 'database' command must have at least one argument: [generate, wipe, dump, delete, get <system_name>]", true)
return 2
elseif arguments[2] == "generate" then
local file = io.open(database, "w+")
file:write("#ssmhub database")
file:close(file)
return 1
elseif arguments[2] == "dump" then
print("= DUMP START =")
for line in io.lines(database) do
print(line)
end
print("= DUMP END =")
return 1
-- 1+
elseif arguments[2] == "get" then
-- 2+
if isEmpty(arguments[3]) then
print("The 'database get' command must have a <name> parameter")
return 0
-- 2-
else -- 3+
local file = io.open(database, "r")
for line in io.lines(file) do -- 4+
local state = ""
local id = ""
local dividersFound = 0
line:gsub(".", function(c) -- 5+
if c == "|" then -- 6+
if dividersFound == 0 then -- 7+
state = state .. c
end -- 7-
if dividersFound == 1 then -- 8+
id = id .. c
end -- 8-
dividersFound = dividersFound + 1
end -- 6-
end) -- 5-
io.close(file)
end -- 4-
end -- 3-
else -- 9+
print("Illegal argument for command. Use 'help' for a list of commands and arguments.")
return 0
end -- 9-
end -- 2-
end -- 1-
function commandHelp()
...
end
-- Main
function main()
arguments = readProgramArguments()
commandArgument = arguments[1]
commandCompleteCode = 0
-- Process help and system commands
if commandArgument == "database" then
commandCompleteCode = commandDatabase()
end
end main()
As #luther pointed out, I had one-too-many end-s in commandDatabase.
This wasn't flagged in my IDE because I had not end-ed commandSystem, so commandSystem was nested inside of it.
To fix: add an end to commandSystem, and remove the end which I tagged '-- 1-'.

NASM local variables - are they, in fact, global macros?

I've been using the %local directive in NASM to define local variables and thus avoid typing [ebp - 8], [ebp - 24] etc. all the time.
However, I noticed that a local variable defined in one function between the preprocessor context %push and %pop is still available in the rest of the code, which may result in unexpected parsing errors.
Here, I've written a minimal example demonstrating the problem:
struc Rect
.left resd 1
.top resd 1
.width resd 1
.height resd 1
endstruc
%define RECT(x) g_rect + Rect. %+ x
segment .bss
g_rect resd 4
segment .text
; ================================================
function1:
%push
%stacksize flat
%assign %$localsize 0
%local width:dword ; defines local var "width"
push ebp
mov ebp, esp
sub esp, 4
pusha
; ...
popa
leave
ret
%pop
; ================================================
function2:
push ebp
mov ebp, esp
pusha
; ...
mov eax, RECT(height) ; OK
mov ebx, RECT(width) ; Parse error
; ...
popa
leave
ret
The exact error is:
nasm -f elf -d ELF_TYPE -g test.asm
test.asm:42: error: comma, colon, decorator or end of line expected after operand
Obviously, it happens because width is getting substituted with something else, and if I remove the local param definition, the problem goes away.
As you can see, the variable width is still available after the %pop. This doesn't look very local to me! I'd expect NASM to undefine width when the %pop is executed.
Is there a way to use %local but avoid these leaking macros? At the moment they act as a simple %define statement which is confusing.

Xtext 2.8+ formatter, formatting HiddenRegion with comment

I am using Xtext 2.9 formatter and I am trying to format hiddenRegion which contains comment. Here is part of my document region i am trying to format:
Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
35 0 H
35 15 S ""xxx::a::b"" Refblock:namespace=Namespace
50 0 H
50 1 S "}" Refblock:RCBRACKET
E Refblock PackageHead:Block=Refblock path:PackageHead/Block=Package'xxx_constants'/head=Model/packages[0]
51 0 H
51 1 S ":" PackageHead:COLON
E PackageHead Package:head=PackageHead path:Package'xxx_constants'/head=Model/packages[0]
52 >>>H "\n " Whitespace:TerminalRule'WS'
"# asd" Comment:TerminalRule'SL_COMMENT'
15 "\n " Whitespace:TerminalRule'WS'<<<
B Error'ASSD' Package:expressions+=Expression path:Package'xxx_constants'/expressions[0]=Model/packages[0]
67 5 S "error" Error:'error'
72 1 H " " Whitespace:TerminalRule'WS'
and corresponding part of the grammar
Model:
{Model}
(packages+=Package)*;
Expression:
Error | Warning | Enum | Text;
Package:
{Package}
'package' name=Name head=PackageHead
(BEGIN
(imports+=Import)*
(expressions+=Expression)*
END)?;
Error:
{Error}
('error') name=ENAME parameter=Parameter COLON
(BEGIN
(expressions+=Estatement)+
END)?;
PackageHead:
Block=Refblock COLON;
Problem is that when i try prepend some characters before error keyword
for example
error.regionFor.keyword('error').prepend[setSpace("\n ")]
This indentation is prepended before the comment and not behind it. This results into improper formatting in case of single line comment before the 'error' keyword.
To provide more clarity, here is example code from my grammar and description of desired behavior:
package xxx_constants {namespace="xxx::a::b"}:
# asd
error ASSD {0}:
Hello {0,world}
This is expected result: (one space to the left)
package xxx_constants {namespace="xxx::a::b"}:
# asd
error ASSD {0}:
Hello {0,world}
and this is the actual result with prepend method
package xxx_constants {namespace="xxx::a::b"}:
# asd
error ASSD {0}:
Hello {0,world}
As the document structure says, the HiddenRegion is in this case is the statement:
# asd
error
How can i prepend my characters directly before the keyword 'error' and not before the comment? Thanks.
I assume you're creating an indentation-sensitive language, because you're explicitly calling BEGIN and END.
For indentation-sensitive language my answer is: You'll want to overwrite
org.eclipse.xtext.formatting2.internal.HiddenRegionReplacer.applyHiddenRegionFormatting(List<ITextReplacer>)
The methods append[] and prepend[] you're using are agnostic to comments and at a later time applyHiddenRegionFormatting() is called to decide how that formatting is weaved between comments.
To make Xtext use your own subclass of HiddenRegionReplacer, overwrite
org.eclipse.xtext.formatting2.AbstractFormatter2.createHiddenRegionReplacer(IHiddenRegion, IHiddenRegionFormatting)
For languages that do not do whitespace-sensitive lexing/parsing (that's the default) the answer is to not call setSpace() to create indentation or line-wraps.
Instead, do
pkg.interior[indent]
pkg.regionFor.keyword(":").append[newLine]
pkg.append[newLine]