Python's if __name__=="__main__" idiom for GNU Smalltalk? - smalltalk

Does such a thing exist?
Ruby:
if __FILE__ == $0
main
end
Perl:
unless(caller) {
main;
}
Lua:
if type(package.loaded[(...)]) ~= "userdata" then
main(arg)
else
module(..., package.seeall)
end

Exotic multiline shebangs and argv trickery do the job.
See Rosetta Code.
scriptedmain.st:
"exec" "gst" "-f" "$0" "$0" "$#"
"exit"
Object subclass: ScriptedMain [
ScriptedMain class >> meaningOfLife [ ^42 ]
]
| main |
main := [
Transcript show: 'Main: The meaning of life is ', ((ScriptedMain meaningOfLife) printString); cr.
].
(((Smalltalk getArgc) > 0) and: [ ((Smalltalk getArgv: 1) endsWith: 'scriptedmain.st') ]) ifTrue: [
main value.
].

Related

Removing delimited block of code between patterns using bash (regex, sed)

I have a file with visibility declarations, spanning over one or more lines, that I want removed only if they are in a test block.
i.e. input.txt:
test(
srcs = [
"test1",
],
visibility = [
"common",
],
deps = [ "deps" ],
)
test(
srcs = [
"test2",
],
visibility = [ "common" ],
)
i.e. output:
test(
srcs = [
"test1",
],
deps = [ "deps" ],
)
test(
srcs = [
"test2",
],
)
The visibility lines could be inside other blocks i.e. etc(...), in which case they should not be removed. i.e.:
etc(
src = [
"etc",
],
# should not be removed because it's not inside a test(...) block
visibility = [
"common",
],
)
This is what I have tried, however, this only matches visibility blocks spanning over a single line:
#!/bin/bash
#remove_lines.sh
remove_visibility_lines () {
start_pattern="test"
end_pattern=")"
pattern_to_remove="visibility = \[.*\],"
sed "/${start_pattern}/,/${end_pattern}/{/${pattern_to_remove}/d}" "$1"
}
remove_visibility_lines $1
$ ./remove_lines.sh input.txt
I've tried several ways to get this to remove visibility spanning over multiple block, i.e. (.*?) and (\_.*), but I can't seem to get it to work.
Please help?
Question is similar to:
Using sed to delete all lines between two matching patterns , however, in my case I have patterns nested inside patterns. I.e.: you only look inside the test(...) block, and only inside those blocks you remove the visibility = [...], blocks.
With awk, assuming no nested () characters:
awk '/^test\(/{f=1} $0==")"{f=0}
f && /visibility = \[/{v=1} !v; /],/{v=0}' ip.txt
/^test\(/{f=1} set flag f if a line starts with test(
$0==")"{f=0} clear flag f if a line contains ) only
f && /visibility = \[/{v=1} set flag v if f is also set and a line contains visibility = [
!v; to print input lines only if flag v is not set
/],/{v=0} clear flag v if line contains ],
To pass start and end strings:
$ cat script.awk
index($0, start) == 1 { f = 1 }
$0 == end { f = 0 }
f && /visibility = \[/ { v = 1 }
! v { print }
/],/ { v = 0 }
$ awk -v start='test(' -v end=')' -f script.awk ip.txt

if-else on arguments in npm run script

I would like to call different other scripts, depending on whether a paramter is given or not:
"paramtest": "if [ -z $1 ]; then echo Foo $1; else echo Bar; fi",
npm run paramtest
should give "Bar".
npm run paramtest -- whatever
should give "Foo whatever".
However in practice I only get: (The parameter is added to the whole line, not 'passed in')
> if [ -z $1 ]; then echo Foo; else echo Bar; fi "whatever
sh: 1: Syntax error: word unexpected
What can I do better?
Essentially I am after running full test suite / only individual test with the same command...
"test" : "if [ -z $1 ]; then mocha ./test/**/*.test.js; else mocha $1
Wrapping it in a shell function should do the trick:
"test": "f() { if [ $# -eq 0 ]; then mocha './test/**/*.test.js'; else mocha -- \"$#\"; fi; }; f"
Note that I changed the if condition and the else branch slightly so you can specify multiple file arguments if necessary.
A more succinct method:
"test": "f() { mocha -- \"${#:-./test/**/*.test.js}\"; }; f"
Using a shell function this way might look familiar, as the same technique is often used for git aliases.
Detailed Explanation
Let's use this script for demonstration:
"scripts": {
"myscript": "if [ \"$1\" = one ]; then printf %s\\\\n \"$#\"; else echo false; fi"
}
Here if the first argument is "one", we print all the arguments, and otherwise we print "false". We are of course assuming that npm run-script is using an sh-like shell, and not, e.g., Windows' cmd.exe.
I can't see anything in the npm documentation specifically detailing how arguments are passed to the script, so let's take a look at the source code (npm v6.14.7 at the time of writing). It seems that the script is joined with its arguments here and is then executed here. Essentially, npm run myscript -- one two three becomes
sh -c 'if [ "$1" = one ]; then printf %s\\n "$#"; else echo false; fi "one" "two" "three"'
Our arguments one two three are simply quote-escaped and concatenated to the script command. In terms of the shell grammar, this means that they are ending up as arguments to fi. sh of course rejects this because fi is just a builtin to end if and takes no arguments.
Our goal is something more like
sh -c 'if [ "$1" = one ]; then printf %s\\n "$#"; else echo false; fi' sh "one" "two" "three"
Here one, two, and three are arguments to sh itself and thus become the argument variables $1, $2, and $3 in the given script. npm doesn't let us do this directly, but we can accomplish the same thing by wrapping our script in a shell function:
"scripts": {
"myscript": "f() { if [ \"$1\" = one ]; then printf %s\\\\n \"$#\"; else echo false; fi; }; f"
}
The script here ends with an invocation of the function, so npm will end up concatenating the arguments to this invocation, ultimately calling the function as f "one" "two" "three":
sh -c 'f() { if [ "$1" = one ]; then printf %s\\n "$#"; else echo false; fi; }; f "one" "two" "three"'

How do I get the current module/script/file name in GNU Smalltalk?

GNU Smalltalk omits the script name in argv.
#!/usr/bin/env gst -f
| argv program |
argv := Smalltalk arguments.
(argv size) > 0 ifTrue: [
program := argv at: 1.
Transcript show: 'Program: ', program; cr.
] ifFalse: [
Transcript show: 'argv = {}'; cr.
]
$ ./scriptname.st
argv = {}
I see two ways to get the script name:
Track down some Smalltalk method which returns the script name akin to Perl's variable $0.
Track down syntax for a multiline shebang and force GST to supply the scriptname as the first member of argv. Here's an example in Common Lisp.
It seems the best that can be done is use shebangs to force the script name to ARGV, then check whether Smalltalk getArgv: 1 ends with a hardcoded string.
Posted here and on Rosetta Code.
"exec" "gst" "-f" "$0" "$0" "$#"
"exit"
Object subclass: ScriptedMain [
ScriptedMain class >> meaningOfLife [ ^42 ]
]
| main |
main := [
Transcript show: 'Main: The meaning of life is ', ((ScriptedMain meaningOfLife) printString); cr.
].
(((Smalltalk getArgc) > 0) and: [ ((Smalltalk getArgv: 1) endsWith: 'scriptedmain.st') ]) ifTrue: [
main value.
]
You can ask the current method where it comes from: thisContext method methodSourceFile printNl.

Problem with context and property

Let's say I want to generate this output:
public String toString() {
return this.getFirstName() + "," + this.getLastName() + "," + this.getAge();
}
from the template below and a custom recursive build-markup function:
template-toString: {this.get<%property%>() <%either not context.build-markup/EOB [{+ "," +}][""]%> }
build-markup/vars template-toString [property] ["FirstName" "LastName" "Age"]
My problem is to avoid the last element to be concatenate with {+ "," +}
My idea was to use a context.build-markup with an EOB property (End Of Block) that would be set to true when last element is processed. Then I could use in template-toString above either not context.build-markup/EOB [{+ "," +}][""] to concatenate or not with {+ "," +} :
context.build-markup: context [
EOB: false
set 'build-markup func [
{Return markup text replacing <%tags%> with their evaluated results.}
content [string! file! url!]
/vars block-fields block-values
/quiet "Do not show errors in the output."
/local out eval value n max i
][
out: make string! 126
either not vars [
content: either string? content [copy content] [read content]
eval: func [val /local tmp] [
either error? set/any 'tmp try [do val] [
if not quiet [
tmp: disarm :tmp
append out reform ["***ERROR" tmp/id "in:" val]
]
] [
if not unset? get/any 'tmp [append out :tmp]
]
]
parse/all content [
any [
end break
| "<%" [copy value to "%>" 2 skip | copy value to end] (eval value)
| copy value [to "<%" | to end] (append out value)
]
]
][
n: length? block-fields
self/EOB: false
actions: copy []
repeat i n [
append actions compose/only [
;set in self 'EOB (i = n)
set in system/words (to-lit-word pick (block-fields) (i)) get pick (block-fields) (i)
]
]
append actions compose/only [
append out build-markup content
]
foreach :block-fields block-values actions
if any [(back tail out) = "^/" (back tail out) = " " (back tail out) = "," (back tail out) = ";" (back tail out) = "/" (back tail out) = "\"] [
remove back tail out
]
]
out
]
]
But my attempt failed (so I commented ;set in self 'EOB (i = n) because it doesn't work). How to correct the code to get what I want ?
I'm quite certain you could be achieving your goal in a cleaner way than this. Regardless, I can tell you why what you're doing isn't working!
Your n is the expression length? block-fields, and your repeat loop goes up to n. But block-fields contains the single parameter [property]! Hence, it loops from 1 to 1.
You presumably wanted to test against something enumerating over block-values (in this example a range from 1 to 3) and then handle it uniquely if the index reached 3. In other words, your set in self 'EOB expression needs to be part of your enumeration over block-values and NOT block-fields.
This would have given you the behavior you wanted:
n: length? block-values
i: 1
foreach :block-fields block-values compose/only [
set in self 'EOB equal? i n
do (actions)
++ i
]
This absolutely won't work:
append actions compose/only [
set in self 'EOB (i = n)
set in system/words (to-lit-word pick (block-fields) (i)) get pick (block-fields) (i)
]
...because you are dealing with a situation where i and n are both 1, for a single iteration of this loop. Which means (i = n) is true. So the meta-code you get for "actions" is this:
[
set in self 'EOB true
set in system/words 'property get pick [property] 1
]
Next you run the code with a superfluous composition (because there are no PAREN!s, you could just omit COMPOSE/ONLY):
append actions compose/only [
append out build-markup content
]
Which adds a line to your actions meta-code, obviously:
[
set in self 'EOB true
set in system/words 'property get pick [property] 1
append out build-markup content
]
As per usual I'll suggest you learn to use PROBE and PRINT to look and check your expectations at each phase. Rebol is good about dumping variables and such...
You seem to making something simple very complicated:
>> a: make object! [
[ b: false
[ set 'c func[i n] [b: i = n]
[ ]
>> a/b
== false
>> c 1 4
== false
>> a/b
== false
>> c 1 1
== true
>> a/b
== true

Rebol: How to use local variables with build-markup function?

Is there a way to do so including creating an other build-markup function ?
Sadly,build-markup uses only global variables: link text says: Note that variables used within tags are always global variables.
Here's a slightly cranky way of doing it using an inner object (bm-1 demonstrates the problem: a and b are printed with their global values; bm-2 is the cranky work around):
a: "global-a"
b: "global-b"
bm-1: func [a b][
print build-markup "<%a%> <%b%>"
]
bm-2: func [a b][
cont: context [
v-a: a
v-b: b
]
print build-markup "<%cont/v-a%> <%cont/v-b%>"
]
bm-1 "aaa" "bbb"
bm-2 "aaa" "bbb"
REBOL3 has reword rather than build-markup. That is much more flexible.
I've patched the build-markup function to be able to use local contexts:
build-markup: func [
{Return markup text replacing <%tags%> with their evaluated results.}
content [string! file! url!]
/bind obj [object!] "Object to bind" ;ability to run in a local context
/quiet "Do not show errors in the output."
/local out eval value
][
content: either string? content [copy content] [read content]
out: make string! 126
eval: func [val /local tmp] [
either error? set/any 'tmp try [either bind [do system/words/bind load val obj] [do val]] [
if not quiet [
tmp: disarm :tmp
append out reform ["***ERROR" tmp/id "in:" val]
]
] [
if not unset? get/any 'tmp [append out :tmp]
]
]
parse/all content [
any [
end break
| "<%" [copy value to "%>" 2 skip | copy value to end] (eval value)
| copy value [to "<%" | to end] (append out value)
]
]
out
]
Here are some example usages:
>> x: 1 ;global
>> context [x: 2 print build-markup/bind "a <%x%> b" self]
"a 2 b"
>> print build-markup/bind "a <%x%> b" context [x: 2]
"a 2 b"