if | else | while | for | foreach | switch | case | default | break |
null | continue | return | true | false | instanceof | class | function | macro |
A character is an expression that presents java.lang.Character object. The lexical rule is almost same as Java language.
e.g.The followings are special characters.'A'
'\'' ==> ' '\n' ==> LF '\t' ==> TAB '\r' ==> CR '\f' ==> ^L '\b' ==> ^H '\\' ==> \ '\0' ==> NUL
> "foo" "foo" > "\b" "^H"> is prompt.
An integer is not restricted within long precision. The class of the number object is assigned automatically depending on the value. When the integer is between Integer.MAX_VALUE and Integer.MIN_VALUE, the class is java.lang.Integer. When the integer is between Long.MAX_VALUE and Long.MIN_VALUE, the class is java.lang.Long. Otherwise, the number is a java.math.BigInteger object.
In Personal Java environment in which java.math package is not supported, numbers out of long precision are represented as Double objects.
("0" | ["1"-"9"] (["0"-"9"])*)
e.g. 123 12345678901234567890
"#" (["0"-"9","a"-"f","A"-"F"])+ "0" ("X"|"x") (["0"-"9","a"-"f","A"-"F"])+
e.g. #ffff #ffff0000 0xff
When the number is between Byte.MAX_VALUE and Byte.MIN_VALUE, "#ff" style makes Byte object while "0xff" style makes Integer object.
e.g. 123F ==> new Float(123.0) 123f ==> new Float(123.0) 123D ==> new Double(123.0) 123d ==> new Double(123.0) 123.0 ==> new Double(123.0) 123.0f ==> new Float(123.0) 1.23E-4 ==> new Double(1.23E-4) 1.23E-4f ==> new Float(1.23E-4)
e.g. 123.0B ==> new BigDecimal("123.0") 123.0E-4B ==> new BigDecimal("0.00123") 1.2 * 2 ==> 3.5999999999999996 1.2B * 2 ==> 3.6
In Personal Java environment in which java.math package is not supported, the suffix 'B' is ignored.
true false
Class reference can be made as follows.
|
The value of a class reference is a java.lang.Class object for this class. For example,
class java.lang.Object ==> java.lang.Object class
Inner classes are denoted by '$'-separeted names.
class Toplevel$Inner ==> Toplevel.Inner class
On JDK1.1, classes are searched by the class loader which has loaded the Pnuts runtime itself. On JDK1.2, System Class Loader is used. When a ClassLoader is set with Pnuts::setClassLoader() method, it is used as default class loader.
Semantics of following arithmetic operations are basically same as Java.
+ | - | * | / | % |
Here is a type transition table for binary arithmetic operations.
BigDecimal | BigInteger | Double | Float | Long | Integer | |
BigDecimal | BigDecimal | BigDecimal | BigDecimal | BigDecimal | BigDecimal | BigDecimal |
BigInteger | BigDecimal | BigInteger | BigDecimal | BigDecimal | BigInteger | BigInteger |
Double | BigDecimal | BigDecimal | Double | Double | Double | Double |
Float | BigDecimal | BigDecimal | Double | Float | Float | Float |
Long | BigDecimal | BigInteger | Double | Float | Long | Long |
Integer | BigDecimal | BigInteger | Double | Float | Long | Integer |
|
a = 0 ++a ==> 1 a ==> 1
|
a = 0 a++ ==> 0 a ==> 1 a = a++ a ==> 1
== | != | < | <= | > | >= |
If both parameters are Number objects, they are compared each other as a number.
1 == 1.0 ==> true
If one of the parameters is a Numeric object and the other is Number or Numeric, they are compared each other with Numeric.compareTo() method.
If both sides of a comparison operator are String object, the result is based on String.compareTo() method.
e.g."A" < "AB" ==> true
If both paramters are arrays, the elements are compared recursively. In this case, only == and != are valid.
[1,[2,3]] == [1,[2,3]] ==> true [1,2,3] == [1,2] ==> false
If one of the parameters is java.lang.Comparable object, the result is base on Comparable.compareTo() method.
Otherwise, comparison operations are based on Object.equals() method. In this case, only == and != are valid.
e.g.1 == 1 ==> true Object() == Object() ==> false
&& | || | ! |
!(1 == 2) ==> true 1 == 1 && 2 == 2 ==> true 1 == 1 || 1 == 2 ==> true
& | | | ~ | ^ | >> | << | <<< |
1 << 100 ==> 1267650600228229401496703205376
<identifier> = <expression> |
*= | /= | += | -= | %= | &= |
|= | ~= | ^= | >>= | <<= | <<<= |
These operator are applicable to Number objects.
<class> ( <expression>, ... ) |
class java.awt.Point(10, 20) Point = class java.awt.Point Point(10, 20)
|
When <class> is a primitive type or an array type, the value of <expression> is converted to the appropriate type.
When the value of <expression> is instance of <class> no conversion is made.
When <expression> can not be converted to the <class>, ClassCastException is thrown.
When Type Cast is used as a parameter of constructor, instance method, or static method, the type information is used to choose a method/constructor.
Class object of primitive types behave like functions to convert into objects of the corresponding classes.
|
If expression is a string of a decimal number, these functions parse the number and return it. If expression is a Number, it is converted to the specified type. If expression is a character, int(), byte(), and short() return the code point of the character.
e.g.int(" 1 ") ==> 1 int('1') ==> 49
If primitive is java.lang.Character and the result of expression is between 0 and 0xffff, the function returns the character at the code point. Otherwise, an exception is thrown.
e.g.char(65) ==> 'A'
<expression> . <identifier> |
Only public fields can be referred.
e.g.Point = class java.awt.Point pt = Point(10, 20) pt.x ==> 10
Only public fields can be assigned.
e.g.pt.x = 100 pt ==> java.awt.Point[x=100, y=20]
<class> :: <identifier> |
Only public fields can be referred.
e.g.class java.awt.Color::blue
Public fields can be assigned as well.
<expression> . <identifier> ( <expression> , ... ) |
"ABC".length() ==> 3 "ABC".getClass() ==> java.lang.String class
<class> :: <identifier> ( <expression> , ... ) |
class java.lang.System::gc() sys = class java.lang.System sys::gc()
'[' <expression> , ... ']' |
Elements of an array can be of different types.[1, 2, 3]
["one", 1, _char(0x3042), null]
|
e.g. type1 = class java.lang.Object[] ==> java.lang.Object[] class int = Integer::TYPE type2 = int[][] ==> int[][] type
|
e.g. array1 = class java.lang.Object[1] ==> [null] int = Integer::TYPE array2 = int[3][2] ==> [[0, 0], [0, 0], [0, 0]]
[1, 2, 3].length ==> 3
<array> '[' <expression> ']' |
a = [1, 2, 3] a[0] ===> 1 a[0] = 10 a[0] ===> 10
Array elements are assignable.
e.g.a = [1, 2, 3] a[0] = 100 a ===> [100, 2, 3]
|
foo = [1, 2, 3] foo[1..] ==> [2, 3] foo[1, 1] ==> [2]
|
foo = "123" foo[1..] ==> foo.substring(1) ==> "23" foo[1..1] ==> foo.sunstring(1, 2) ==> "2"
|
foo = "123" foo[1] ==> foo.charAt(1) ==> '2'
<array> + <array> |
[1, 2, 3] + [4, 5, 6] ===> [1, 2, 3, 4, 5, 6]
Type of resulting array is same as the first array. Therefore, all elements of the second array must be of the component type of the first array.
int[0] + [1, 2, 3] ===> [1, 2, 3]
[1, [2, ["yes", "no"], null]] ==> new Object[]{ new Integer(1), new Object[]{ new Integer(2), new Object[]{ "yes", "no" }, null } }
<string> + <string> |
foo = "ABC" bar = "DEF" foo + bar ==> "ABCDEF"
|
foo = [1, 2, 3] if (foo.length > 5){ 1 } else if (foo.length > 4){ 2 } else { 3 } ==> 3 value = if (foo.length > 3) 1 else 2 value ==> 2
|
sum = 0 foreach i [1, 2, 3] { sum = sum + i } sum ==> 6 sum = 0 vec = class java.util.Vector() vec.addElement(1) vec.addElement(2) vec.addElement(3) foreach i (vec.elements()) { sum = sum + i } sum ==> 6
while ( <boolean expression> ) <expression-block> |
while (sum > 0){ sum = sum - 1 }
for ( [ <identifier> = <expression> , ... ] ; [ <expression> ] ; [ <expression> , ... ] ) <expression-block> |
j = 0 for (i = 0; i < 5; ++i){ j = j + i } for (; j > 0; j--){ println(j) }
|
Matching is done by the equals() method.
|
"break" without <expression> is equivalent to "break null".
|
for (i = 0; i < 10; ++i){ if (i > 2){ continue } println(i) }
|
When return is evaluated in a function, the function returns the value of <expression>. When return is evaluated at the top level, the interpreter session returns the value of <expression>, which is equivalent to quit(expressoin).
"return" without <expression> is equivalent to "return null".
|
Functions are identified by the name and the number of parameters. If <identifier> is omitted, a unique name is generated automatically for the function.
e.g.f = function (e) e.getSource().dispose() f ==> function _function_497922801(e)
Nested functions can be defined. Nested functions are defined in the scope just as local variables in functions. Inner functions are are denoted by '$'-separated names which reflect the scope hierarchy.
e.g.function f1(){ function g2() 100 function f2(){ function f3() g2() } } f1() ==> function f1$f2() f1()() ==> function f1$f2$f3() f1()()() ==> 100
Functions have static (lexical) scope.
e.g. (1)function f(){ x = 0 function set(value) x = value function get() x ::set = set ::get = get } f() set(100) get() ==> 100 f() get() ==> 0e.g. (2)
function inc(x) ++x function abs(x) if (x < 0) -x else x function compose (f1, f2) return (function () f1(f2(x))) compose(inc, abs)(-100) ==> 101 compose(abs, inc)(-100) ==> 99
If a function definition has one parameter followed by "[]", the function can have arbitrary number of parameter. In that case, passed arguments are refered as an array in the function body. When a function with certain number of parameter is called, it is always called even if one with an arbitrary number of parameter is defined.
e.g.function f(args[]) args f(1) ==> [1] f(1,2,3) ==> [1, 2, 3] function f(arg) arg f(1) ==> 1 f(1,2,3) ==> [1, 2, 3]
<function> ( <expression>, ... ) |
A function call causes function body to be evaluated replacing the formal parameter with the actual arguments.
Primitive types of Java, int, long, etc., are wrapped by Number subclasses. When a Java method returns a primitive type value, the script takes the value as a Wrapper object similar to java.lang.Integer. If Java methods take primitive type parameter, the actual parameter from script is converted to fit the signature.
e.g.str = "ABCDE" str.charAt(1) ==> 'B'
Array types in Java, such as int[], byte[], are represented as is in Pnuts. When Java methods take an array parameter and type conversion from the actual parameter is needed, a new array is created to fit the method signature then applied to the method.
e.g.array = "Hello".toCharArray() ==> ['H','e','l','l','o'] array.getClass() ==> char[] type String(['H','e','l','l','o']) ==> "Hello"
|
Package provides naming scopes. Every function and variable belongs to a package. There is always a package which is called "current package". The current package is specified with package(pkg) function and can be retrieved with the package() function. These functions are explained later. If package(pkg) function has not been called , following functions and top-level variables belong to global package, which has the name "". Names in global package are always visible if the name is not hidden by other scopes.
Variables and functions in aPackage can be referred by "aPackage :: variableName". Functions are executed in the package in which the functions are defined.
If package name and variable that refers to Class object conflict, the package has precedence. To avoid the conflict, Class object should be assigned to a variable whose name starts with an upper case character, while package name should start with a lower case.
name value int Integer::TYPE char Character::TYPE byte Byte::TYPE short Short::TYPE long Long::TYPE float Float::TYPE double Double::TYPE boolean Boolean::TYPE pnuts_version The version name pnuts_load_path described in Pnuts User's Guide EMPTY When this is assigned to a variable, the variable is deleted from the package.
package::var |
|
toplevel variables |
|
functions |
|
for/foreach |
package("foo") a = 1 function t1 (){ foo::a = -1 ::a = -2 a = 2 for (a = 0; a < 10; ++a){ println(a) } println(a + " should be 2") } function t2(){ a = 3 t1() println(a + " should be 3") function t3(){ a = 4 } t3() println(a + " should be 4") } println(a + " should be 1") t2() println(a + " should be -1") println(::a + " should be -2")
Ten primitive operations are provided as built-in functions, which can not be redefine by the User.
import package getContext throw catch load loadFile eval defined quit
|
import() registers <className> for the current context. If an undefined variable is referenced, the variable is checked if the name is registered with this function.
<className> can be wildcard like "java.awt.*".
Initially "java.lang.*" and default package ("*") are registered. Classes are searched from the latest imported class. But a wildcard name does not override a specific class name.
Object ==> java.lang.Object class import("org.omg.CORBA.*") Object ==> org.omg.CORBA.Object interface import("java.lang.Object") Object ==> java.lang.Object class import("org.omg.CORBA.*") Object ==> java.lang.Object class
When the parameter <className> is null, all class names are unregistered.
|
Get the list of imported classes and package names.
import() ==> ["java.lang.*", "*"] import("org.omg.CORBA.Object") import() ==> [org.omg.CORBA.Object interface, "java.lang.*", "*"] import(null) import() ==> []
package ( <nameString> or <package> ) |
Enter the specified package. If <nameString> is specified but the package does not exist, it creates the package and enters the package.
e.g.(1)e.g.(2)package() ==> package "" a = 1 package("foo") ==> enter package "foo" a ==> 1 a = 2 a ==> 2 ::a ==> 1 package("") ==> enter package "" a ==> 1 foo::a ==> 2
package() ==> package "" function f() 1 package("foo") ==> enter package "foo" f() ==> 1 function f() 2 f() ==> 2 ::f() ==> 1 package("") ==> enter package "" f() ==> 1 foo::f() ==> 2
The return value is of pnuts.lang.Package class.
package(<nameString>) is equivalent to package(class pnuts.lang.Package::getPackage(<nameString>)).
package(null) is equivalent to package(class pnuts.lang.Package())
package () |
Get the current package.
e.g.package() ==> package "" package("foo") package() ==> package "foo" package("") package() ==> package ""
getContext () |
Context is an internal object in Pnuts interpreter. It retains following information;
The result of getContext() is of pnuts.lang.Context class, which is described in the API doc.
e.g.getContext().setOutputStream(System::out)
Refer to Pnuts User's Guide for more details.
|
loadFile() reads a script from local file system. load() reads a script from search path bound to pnuts_load_path. Both functions return the result of the last expression.
e.g.loadFile("/home/my.pnut") load("examples/pnutsLayout.pnut")
Refer to Pnuts User's Guide for more details.
|
foo = "YES" eval("foo.length()") ==> 3
When eval() defines a variable in string, the variable is defined in the scope in which eval() is called().
function f1(){ eval("function f2() true") } f = f1() ==> f1$f2 f() ==> true f2 ==> f2 is not defined
When context is specified, string is evaluated in the Context.
c = getContext().clone() function f(){ eval("x = 100", c) } f() x ==> 100
When package is specified, string is evaluaged in a new Context that package is the current package.
eval("a = 100", "pkg") pkg::a ==> 100
throw ( <exception or string> ) |
throw() throws the specified exception or RuntimeException if String object is specified.
e.g.throw("be careful") throw(FileNotFoundException())
catch ( <exceptionClass>, <function> ) |
catch() defines an exception handler in the stack frame. When one of the registered exception is thrown, the corresponding handler is executed and the function immediately returns the result of the handler.
The scope of exception handler is same as that of variables.
e.g.function func(){ catch(FileNotFoundException, function (e) "Don't care") println(1) FileInputStream("file doesn't exist") println(2) } > func() 1 "Don't care" > FileInputStream("file doesn't exist") java.io.FileNotFoundException : "file doesn't exist"
When <function> is null the exception handler is unregistered.
|
defined() checks if the specified symbol has been defined at an arbitrary scope.
e.g.(1) > defined("X") false > X = 100 100 > defined("X") true > X = null null > defined("X") true
e.g.(2) > function f(Z) defined("Z") function f(Z) > defined("Z") false > f(null) true
defined() also checks availability of a class.
e.g.(3) > defined("Window") false > import("java.awt.Window") null > defined("Window") true
quit ( [ <expression> ] ) |
quit() terminates the evaluation of the current InputStream without reading EOF. For example, this function allows successive evaluations, either from different sources or in different contexts, in an interactive program.
When <expression> is specified as a parameter, the interpreter session returns the value. See Pnuts API for details.
e.g.(in Java) Object ret = Pnuts.load(System.in, true, new Context()); System.out.println("ret = " + ret); (in Pnuts) quit(123) ------------ ret = 123Note that this function can not terminate the entire program when a non-daemon thread, like AWT-EventDispatcher, is running even if the main thread is stoped.
When an object which implements one of special interfaces appears in an arithmetic operation or a member access expression, the corresponding method of the interface is called, so that it simplifies the script in which lengthy method call was needed.
When a pnuts.lang.Numeric object is in an arithmetic operation, the corresponding method is called.
public interface Numeric { Object add(Object o); Object subtract(Object o); Object multiply(Object o); Object divide(Object o); Object negate(); Object inverse(); int compareTo(Object o); int NOT_EQUAL = 2; int LEFT_IS_BIGGER = 1; int RIGHT_IS_BIGGER = -1; int EQUAL = 0; }
When the first operand of an arithmetic operation is pnuts.lang.Numeric object, the corresponding method is called.
e.g.:Numeric n1, n2; n1 + n2 ==> n1.add(n2) n1 - n2 ==> n1.subtract(n2) n1 * n2 ==> n1.multiply(n2) n1 / n2 ==> n1.divide(n2) n1 > n2 ==> n1.compareTo(n2) == LEFT_IS_BIGGER - n1 ==> n1.negate()
The "+" and "*" operators are assumed to be commutative. So if the second operand is Numeric object and the first operand is Number object, the order of the operation is reversed.
When the second operand of "-" is Numeric object and the first operand is Number object, the second operand is negated and then added to the first operand.
When the second operand of "/" is Numeric object and the first operand is Number object, the second operand is inversed and then multiplied to the first operand.
See Pnuts Users Guide for details.package pnuts.lang; public interface QuantityFactory { public Object make(Number number, String unitName); }
Unit name should be registered with Pnuts::registerQuantityFactory(String, QuantityFactory) method. When a defined unit name follows a decimal number literal, the method QuantityFactory.make() of the corresponding QuantityFactory object is called and the return value is the result of the expression.
The result of make() method is typically a Numeric object. In that case the arithmetic operations can be applied to the quantity.
10cm - 1in ==> 7.460cm 0.5cm - 0.1in ==> 0.246cm
See Pnuts Users Guide for details.
Index access to pnuts.lang.Indexed object causes a method call of the interface.
pnuts.lang.Indexed interface is defined as follows.
package pnuts.lang; public interface Indexed { void set(int idx, Object value); Object get(int idx); }e.g.
i1 = anIndexed i1[0] ==> i1.get(0) i1[0] = 18 ==> i1.set(0, 18)
Member access to pnuts.lang.Property object causes a method call of: get() or set().
pnuts.lang.Property interface is defined as follows.
package pnuts.lang; public interface Property { void set(String name, Object value); Object get(String name); }e.g.
p1 = aProperty p1.name ==> p1.get("name") p1.age = 18 ==> p1.set("age", 18)
Note that pnuts.lang.Package class implements pnuts.lang.Property interface. Therefore, dot notation can be used for Package objects.
e.g.package("foo") p1 = package() package("") p1.msg = "yes" ==> p1.set("msg", "yes") <== foo::msg = "yes"
Method call of pnuts.lang.AbstractData object causes a call of the invoke() method.
pnuts.lang.AbstractData interface is defined as follows.
package pnuts.lang; public interface AbstractData extends Property { Object invoke(String name, Object args[]); }e.g.
o1 = anAbstractData o1.work(1,2,3) ==> p1.invoke("work", [1,2,3])
The following is an informal syntax description of the script language.
S ::= COMMAND+ COMMAND ::= EXPR (";" | <EOL> | <EOF>) <EOL> ::= ( '\n' | '\r' | '\r\n' ) EXPR ::= ASSIGN | OPERATION | STATEMENT PRIMARY ::= PREFIX SUFFIX* PREFIX ::= LIST | CLASS | LITERAL | ID | "(" EXPR ")" SUFFIX ::= "[" [ EXPR ] "]" | "(" EXPR_LIST ")" | "." ID | "." ID "(" EXPR_LIST ")" | "::" ID | "::" ID "(" EXPR_LIST ")" OPERATION ::= OPERATION BIN_OP OPERATION | U_OP OPERATION | "++" PRIMARY | "--" PRIMARY | PRIMARY "++" | PRIMARY "--" | "(" ID ( "[" "]" )* ")" EXPR BIN_OP ::= '*' | '+' | '-' | '/' | '&&' | '||' | '^' | '==' | '!='| '<' | '>' | '<=' | '>=' | '>>' | '<<' | '>>>' U_OP ::= '-' | '!' STATEMENT ::= IF | WHILE | FOREACH | FOR | FUNC | BREAK | RETURN ASSIGN ::= PRIMARY ASSIGN_OP <EOL>* EXPR ASSIGN_OP ::= "=" | "*=" | "/=" | "+=" | "-=" | "%=" | "&=" | "~=" | "|=" | "^=" | "<<=" | ">>=" | ">>>=" FUNC ::= 'function' ID '(' ID_LIST ')' <EOL>* BLOCK ID_LIST ::= PARAM ( '[' ']' | ( ',' PARAM )* ) PARAM ::= <EOL>* ID <EOL>* BLOCK ::= EXPR | '{' <EOL>* [ EXPR ( (';' | <EOL>) [ EXPR ] )* ] '}' BREAK ::= 'break' [ EXPR ] RETURN ::= 'return' [ EXPR ] IF ::= 'if' '(' EXPR ')' <EOL>* BLOCK ('else' 'if' <EOL>* BLOCK)* ['else' <EOL>* BLOCK] FOREACH ::= 'foreach' ID ( LIST | '(' EXPR ')' ) <EOL>* BLOCK FOR ::= 'for' '(' [ FOR_INIT ] ';' <EOL>* [ EXPR ] ';' <EOL>* [ FOR_UPDATE ] ')' <EOL>* BLOCK FOR_INIT ::= (ID '=' <EOL>* EXPR) | (FOR_INIT "," <EOL>* FOR_INIT) FOR_UPDATE ::= EXPR | (FOR_UPDATE "," <EOL>* FOR_UPDATE) WHILE ::= 'while' '(' EXPR ')' <EOL>* BLOCK SWITCH ::= 'switch' '(' EXPR ')' <EOL>* '{' <EOL>* ( LABEL SW_BLOCK )* '}' LABEL ::= ('case' EXPR ':' | 'default' ':' ) SW_BLOCK ::= <EOL> ( EXPR (';' | <EOL> ) <EOL>* )* CLASS ::= 'class' <class_name> ('[' ']')* ARRAY_TYPE ::= ID ('[' ']')+ LIST ::= '[' [ EXPR_LIST ] ']' EXPR_LIST ::= <EOL>* EXPR ( ',' <EOL>* EXPR )* <EOL>*
Last modified: Fri Jun 4 00:06:36 JST 1999