erlang macro expansion bug - macros

macro module:
-module(macro).
-include_lib("eunit/include/eunit.hrl").
-define(EXPAND(_T), ??_T).
macro_test() ->
?assertEqual("Test", ?EXPAND(Test)),
?assertEqual("Test.test", ?EXPAND(Test.test)).
Is resulting :
6> c(macro).
{ok,macro}
7> eunit:test(macro).
macro: macro_test (module 'macro')...*failed*
in function macro:'-macro_test/0-fun-1-'/1 (macro.erl, line 9)
**error:{assertEqual_failed,[{module,macro},
{line,9},
{expression,"? EXPAND ( Test . test )"},
{expected,"Test.test"},
{value,"Test . test"}]}
=======================================================
Failed: 1. Skipped: 0. Passed: 0.
error
Am I doing something wrong or is this a known bug?
TIA

You're incorrectly assuming that the Erlang compiler treats Test.test as a single token. If you pass the -P option to erlc and examine the output, you'll see that the preprocessor breaks it into multiple tokens. Here's the interesting part of macro.P produced by erlc -P macro.erl:
macro_test() ->
begin
fun(__X) ->
case "Test" of
__X ->
ok;
__V ->
error({assertEqual,
[{module,macro},
{line,8},
{expression,"? EXPAND ( Test )"},
{expected,__X},
{value,__V}]})
end
end("Test")
end,
begin
fun(__X) ->
case "Test . test" of
__X ->
ok;
__V ->
error({assertEqual,
[{module,macro},
{line,9},
{expression,"? EXPAND ( Test . test )"},
{expected,__X},
{value,__V}]})
end
end("Test.test")
end.

Related

How to exit a REPL from the command line

I'm currently learning Lua and also learning how to work with CMD.
I know how to change a directory path and run my codes and files from that path
but what I don't know and I'm here to ask for is how to get out of a programming language REPL when you start it in CMD
For example to jump into the Lua REPL you should type:
lua53 (--like python3 for the Python language)
then you changed the CMD environment to a Lua compiler and can't access CMD commands such as
dir, cd, cls etc. and everytime when I need to access these commands I have to close the CMD window and open a new one.
Now can you guys tell me am I able to access CMD commands while in the Lua REPL? Or do I have to exit Lua first, and is there any command to exit a REPL?
I'd recommend EOF (Ctrl + D on Unix) rather than SIGKILL (Ctrl + C) because some REPLs (node and python) choose to ignore the latter (perhaps because it's often used for copying text?); python will just print KeyboardInterrupt whereas node will only exit if you press Ctrl + C twice (it will send a message telling you this the first time).
EOF (Ctrl + D) on the other hand immediately exists all REPLs I have used so far.
The Lua REPL stops immediately when it receives either EOF or SIGKILL, so you can use both here.
Edit: As Sowban points out, EOF apparently is entered as Ctrl + Z then Enter in powershell.
You could type ctrl c to exit the process that's running generally.
I suggest to write a REPL by yourself.
But be warned :-)
The main loop with a prompt and the interpreting and executing function/method is mostly the easiest part.
99.99999% is the errorhandling thing.
One of my earliest interpreter language is (A)REXX.
A REPL without any errorhandling is done with...
/* REXX have to start with a comment */
do forever
parse pull input
interpret input
end
Now Lua sandboxed to an _ENV with io and os library and a little bit of errorhandling...
#!/usr/bin/env -S readline-editor /usr/local/bin/lua
-- ^--SHEBANG for Linux --------------------------------------------------------------------------------------
-- interpreter.lua
-- Sandboxed to io and os
-- Lua 5.4 >>-because-> goto label <const> load()
--------------------------------------------------------------------------------------------------------------
local Lua = function(...)
-- Info about OS
io.write(("%s\n"):format(io.popen('uname -a'):read())):flush()
-- Global Setting
debug.setmetatable((1), {__index = math}) -- Add math library to number as methods (like string for strings)
os.setlocale('de_DE.UTF8')
os.setlocale('en_US.UTF8', 'time')
io.write(os.date("[%c]\n" .. ("%s\n"):format(os.setlocale():gsub("%;", "\n")))):flush()
-- io.write(("%s\n"):format(_VERSION)):flush()
-- Label for goto
::lua::
-- Local Setting
local args <const> = args or {...}
local Lua = Lua or true
local cmd = cmd or ""
--------------------------------------------------------------------------------------------------------------
local env <const> = env or setmetatable({os = os, io = io}, -- The _ENV for load()
{__call = function(self, ...)
local self, t = ({...})[1] or self, "" -- First argument becomes self if one
if ({...})[1] == "help" then self = getmetatable(({...})[2]).__index end -- Showing metamethod __index (table)
for k, v in pairs(self) do
t = t .. ("%s => %s\n"):format(k, v)
end
return t
end,
__index = {cg = collectgarbage,
gt = getmetatable,
pairs = pairs,
tn = tonumber,
ts = tostring,
_V = ("%s \27[1;" .. (31):random(36) .. "m(sandboxed)\27[0m"):format(_VERSION)},
__tostring = function(self) return self._V end}) -- end env
--------------------------------------------------------------------------------------------------------------
local prompt = prompt or setmetatable({}, {__tostring = function() return getmetatable(env).__index._V .. "> " end})
local name <const> = name or _VERSION
local result = result or true
--------------------------------------------------------------------------------------------------------------
while Lua do
io.write(tostring(prompt))
cmd = io.read() or 'quit'
if cmd == "quit" then Lua, result = true, true break end
Lua, result = pcall(load("return " .. cmd or false, name, "t", env))
if Lua and result then
io.write(("%s"):format(tostring(result)))
else
goto exception
end
end
--------------------------------------------------------------------------------------------------------------
-- Errorhandler
::exception::
io.write(("%s\n"):format("Exception"))
if not Lua or not result then
io.output(io.stderr)
io.write(("[%s][%s][%s]\n\27[1;31m>>-Exception->\27[0m %s\n"):format(os.date(), _VERSION, cmd, result)):flush()
io.output(io.stdout)
collectgarbage()
goto lua
end
goto lua
end
--------------------------------------------------------------------------------------------------------------
-- EXAMPLE ---------------------------------------------------------------------------------------------------
-- Lua = require("interpreter")
Lua() -- UNCOMMENT-> For direct execution like: /bin/lua interpreter.lua
return Lua -- UNCOMMENT-> For: Lua = require("interpreter")
Ctrl&C quits hardly
Ctrl&D is only an exception
...and os.exit(0) do an clean exit with returncode 0.
Impression

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

Overnight script returning scripting error 201 at Set Field

I have a script setup to run overnight on my Filemaker server, but every night it returns this error:
Schedule "Recheck All Flags" scripting error (201) at "MasterDatabase : Recheck All Flags : ### : Set Field
Where ### is just a random number, probably the line in the script. In the above example let's just say it was 398.
I can't copy and paste the script to here, but it is very simple. It's one big loop over all records in the database and it checks a bunch of If statements. It looks like:
If [MasterDatabase::Start date = ""]
Set Field [MasterDatabase::Flagged for Discrepancy; "Yes"]
Commit Records/Requests [With dialog:Off]
End If
Why might this be failing when running on the server overnight?
Make sure you tell the server to go to the correct layout before the script starts, when the server starts up the script it will do the work on the layout that it is configured for the DB to open by default.
You can do something like:
If [ $$PLATFORM = "Server" ]
(Go to Layout X)
End If
The global variable $$PLATFORM can be defined in your On First Window Open Script like so:
Case (
PatternCount ( Get ( ApplicationVersion ) ; "Server" ) ;
"Server" ;
PatternCount ( Get ( ApplicationVersion ) ; "Data API" ) ;
"Data API" ;
Get ( SystemPlatform ) = 4 ;
"Web" ;
Choose (
Get ( Device ) ;
"Desktop" ; // 0 - Unknown
"Desktop" ; // 1 - Mac
"Desktop" ; // 2 - PC
"Tablet" ; // 3 - Tablet
"Phone" // 4 - Phone
)
)
If you already have that covered then make sure whatever user you are using for the PSOS has access to that layout, records scripts and fields.

CoffeeScript: Spurious comma in comprehension joined by empty separator

I wanted a convenience function to catenate jQuery parent > child selector strings. I can't get the following to work in CS 1.10.0 (also tested in 1.7.1). What am I doing wrong?
pcsel = (parent_sel, child_sels...) ->
### Uitlity for forming parent > child selector string ###
childchain = [" > " + child for child in child_sels]
parent_sel + childchain.join('')
console.log pcsel("foo", "bar") # OK. yields "foo > bar"
console.log pcsel("foo", "bar", "glop") # BAD. yields "foo > bar, > glop"
# Sanity check
console.log "foo" + [" > bat", " > glop"].join('') # OK. yields "foo > bar > glop"
Thanks!
(I've also posted this as an issue in the CS repository)
A loop comprehension:
expr for e in array
evaluates to an array. That means that this:
[ expr for e in array ]
is actually a single element array whose first (and only) element is the array from the loop. More explicitly:
i for i in [1,2,3]
is [1,2,3] but this:
[ i for i in [1,2,3] ]
is [[1,2,3]].
Your problem is that childchain in pcsel ends up with an extra level of nesting and the stringification from the join call adds unexpected commas.
The solution is to fix pcsel:
childchain = (" > " + child for child in child_sels)
# -----------^-------------------------------------^
You need the parentheses (not brackets) to get around precedence issues; parentheses (()) and brackets ([]) serve entirely different functions so you need to use the right ones.
From what I can tell, the behavior you're seeing is what's to be expected. Here's how your code behaves if you replace the splat with an explicit array:
coffee> ["> " + ['bar']] # => ['> bar']
coffee> ["> " + ['bar', 'baz']] # =>['> bar,baz']
You'll also see the same behavior in node:
> [">" + ['bar']] // => ['>bar']
> ["> " + ['bar', 'baz']] // => ['> bar,baz']
You could achieve what you're after using multiple calls to .join, or by doing something like this:
pcsel = (parent_sel, child_sels...) ->
child_sels.reduce (memo, sel) ->
memo + " > #{sel}"
, parent_sel
console.log pcsel("foo", "bar") # => foo > bar
console.log pcsel("foo", "bar", "glop") # => foo > bar > glop
console.log pcsel("foo", "bar", "glop", "baz") # => foo > bar > glop > baz

LWT simple interaction with a subprocess

Here is a simple program that uses the Unix module to interact with a subprocess. I just launch a cat shell command, send it a string and read it back:
#load "unix.cma";; (* Needed if you are in the toplevel *)
let () =
let sin, sout, serr = Unix.open_process_full "cat" [||] in
output_string sout "test\n";
flush sout;
input_line sin |> print_string;
flush stdout;
Unix.close_process_full (sin, sout, serr) |> ignore;;
Recently I started studying the Lwt library, and I wanted to reproduce the same functionality with it. I though that the following should have exactly the same result:
#use "topfind";; (* *)
#thread;; (* Also only for the toplevel *)
#require "lwt.simple-top";; (* *)
let () =
let open Lwt in
let process = Lwt_process.open_process_full ( "cat" , [||] ) in
Lwt_io.write_line process#stdin "test\n"
>>= ( fun () -> Lwt_io.flush process#stdin )
>>= ( fun () -> Lwt_io.read process#stdout )
>>= ( fun str -> Lwt_io.print str )
>>= ( fun () -> Lwt_io.flush Lwt_io.stdout )
|> Lwt_main.run
But it doesn't work as I expect it to -- apparently it reads and then prints an empty string.
I guess I have some fundamental confusion on how Lwt should work, but I cannot figure it out. Can someone show me how one can communicate with a subprocess using Lwt?
Use Lwt_process.shell to make a proper command, in your case, the proper command is the following:
Lwt_process.shell "cat";;
- : Lwt_process.command = ("", [|"/bin/sh"; "-c"; "cat"|])
Also, I suspect, that after you will run your program in a proper way, you will be wondering, why is your program blocking. This reason is because cat process will not finish until you write an EOF to it's input channel. That's why the Lwt_io.read call will not ever finish. A solution would be to close the stdin channel.