Manual of Fun Programming Language
0. What is Fun?
Fun is a simple, structured, imperative, dynamic, object-oriented, functional and high-level programming language. Fun was developed by Wisdom ZHANG in 2010 as a scripting language to reuse resources of Pascal.
Fun's syntax is simple and clear, it comes from Ada and was changed for easy to use and input. Fun's exception handling syntax is similar to Delphi. Fun is dynamic typing and supports type conversions automatically when needed. Fun supports class-based object-oriented programming. Fun supports first-class functions functional programming, looks like Lisp and Javascript. Fun implements a simple and lightweight reference counting garbage collector. Fun's regular expressions handling is Perl-Compatible. Fun provides simple and powerful list process, it looks like arrays, associative arrays, hash tables, dictionaries or mappings too.
1. Control flow: Structured programming for Ada-like script
Fun uses syntax of Ada. The syntax of Ada is very clear and easy to read and write. The statements are semicolon terminated. Fun differs from Ada, such as "when ... =>" to "when ... do", add "while ... do" and "for ... do", and so on.
Exception handling syntax is changed from Delphi. It supports "except" and "finally" at the same time. Remove "on" in "except" due to dynamic typing.
1.1 Conditional statements: if/case
Includes "if..then" and "case..when".
1.1.1 if
- ########################################
- # if
- ########################################
- # if expression then cmds
- # { elsif expression then cmds } 0..n
- # [ else cmds ] 0..1
- # end if;
- ########################################
-
- if true then
- ..?. true; # HIT
- end if;
-
- if false then
- ..?. false;
- else
- ..?. true; # HIT
- end if;
-
- if false then
- ..?. false;
- elsif true then
- ..?. true; # HIT
- else
- ..?. "What's wrong with you?";
- end if;
-
- ########################################
- # 1. Comments start with "#" or "//"
- # Comment block: #* ... #
- # 2. "true", "false" are keywords
- # 3. "?." means output (append extra \r\n)
- # "?," means output (append a blank' ')
- # "?:" means output (append a TAB )
- # "?" means output (without any extra)
- ########################################
True
True
True
Download this DEMO
1.1.2 case
- ########################################
- # case
- ########################################
- # case expression is
- # { when value do cmds }+ 1..n
- # [ else cmds ] 0..1
- # end case;
- ########################################
-
- var exp = 1;
-
- case exp is
- ..when 1 do
- ....?. 1; # HIT
- ..when 2 do
- ....?. 2;
- ..when 3 do
- ....?. 3;
- ..else
- ....?. 'else';
- end case;
-
- exp = "Hello";
-
- case exp is
- ..when "Good" do
- ....?. @ & ' morning!';
- ..when "Bad" do
- ....?. @ & ' command or file name!';
- ..when "Hello" do
- ....?. @ & ', world!'; # HIT
- ..else
- ....?. @ & ' not found!';
- end case;
-
- ########################################
- # 1. Value in "when" may be anything ([...] or /.../)
- # 2. "@" is a built-in variable in "case"
- # @ <- expression in "case ... is"
- # 3. "&" means string concat
- # 4. String quoted with ' or " or `
- ########################################
1
Hello, world!
Download this DEMO
1.2 Loop statements: loop
- ########################################
- # loop
- ########################################
- # loop
- # cmds
- # end loop;
- ########################################
-
- var i = 1;
-
- loop
- ..?. i;
- ..exit;
- end loop;
-
- loop
- ..exit when i > 9;
- ..? i;
- ..i += 1;
- end loop;
- ?. '..DONE';
-
- i = 9;
-
- loop
- ..i -= 1;
- ..exit when i <= 0;
- ..next when i mod 2 = 1;
- ..? i;
- end loop;
- ?. '..DONE';
-
- ########################################
- # 1. "exit" means exit the loop
- # "exit when" means when ... exit ...
- # "next" means skip to next loop
- # "next when" means when ... next ...
- # 2. v += ... means v = v + ...
- # v -= ... means v = v - ...
- # v *= ... means v = v * ...
- # v &= ... means v = v & ...
- ########################################
1
123456789..DONE
8642..DONE
Download this DEMO
1.2.1 while
- ########################################
- # loop-while
- ########################################
- # while expression loop
- # cmds
- # end loop;
- ########################################
-
- var i = 1;
-
- while i <= 9 loop
- ..? i;
- ..i += 1;
- end loop;
- ?. '..DONE';
-
- i = 9;
-
- while i > 0 do
- ..? i;
- ..i -= 1;
- end do;
- ?. '..DONE';
-
- ########################################
- # 1. while ... "loop" -> while ... "do"
- # end "loop" -> end "do"
- ########################################
123456789..DONE
987654321..DONE
Download this DEMO
1.2.2 for
- ########################################
- # loop-for
- ########################################
- # for v = e1 to e2 [step e3] loop
- # cmds
- # end loop;
- ########################################
-
- for i = 1 to 9 loop
- ..? i;
- end loop;
- ?. '..DONE';
-
- for i = 9 to 1 step -1 do
- ..? i;
- end do;
- ?. '..DONE';
-
- ########################################
- # 1. for ... "loop" -> for ... "do"
- # end "loop" -> end "do"
- # 2. v is a built-in variable in loop
- ########################################
123456789..DONE
987654321..DONE
Download this DEMO
1.2.3 for-in
- ########################################
- # loop-for-in
- ########################################
- # for v in expression loop
- # cmds
- # end loop;
- ########################################
- # for k : v in expression loop
- # cmds
- # end loop;
- ########################################
- # for k : v, i in expression loop
- # cmds
- # end loop;
- ########################################
- # for v, i in expression loop
- # cmds
- # end loop;
- ########################################
-
- for i in [1, 2, 3, 4, 5] loop
- ..? i;
- end loop;
- ?. '..DONE';
-
- for i in [5, 4, 3, 2, 1] do
- ..? i;
- end do;
- ?. '..DONE';
-
- for k: i in [a: 1, b: 2, c: 3] do
- ..?, k;
- ..?, i;
- end do;
- ?. 'DONE';
-
- for k: i, j in [a: 1, b: 2, c: 3] do
- ..?, k;
- ..?, i;
- ..?, j;
- end do;
- ?. 'DONE';
12345..DONE
54321..DONE
a 1 b 2 c 3 DONE
a 1 0 b 2 1 c 3 2 DONE
Download this DEMO
1.3 Exceptions: try
- ########################################
- # try
- ########################################
- # try cmds
- # [ except cmds ] 0..1
- # [ finally cmds ] 0..1
- # end try;
- ########################################
-
- try
- ..? 'try..'; # HIT
- except
- ..? 'except..';
- end try;
- ?. 'DONE';
-
- try
- ..? 'try..'; # HIT
- finally
- ..? 'finally..'; # HIT
- end try;
- ?. 'DONE';
-
- try
- ..? 'try..'; # HIT
- except
- ..? 'except..';
- finally
- ..? 'finally..'; # HIT
- end try;
- ?. 'DONE';
-
- try
- ..? 'try..'; # HIT
- ..? 1 / 0;
- ..? '1 / 0';
- except
- ..? 'except..'; # HIT
- ..? @ & '..'; # HIT
- finally
- ..? 'finally..'; # HIT
- end try;
- ?. 'DONE';
-
- try
- ..? 'try..'; # HIT
- ..raise "Stop!";
- ..? 'Stop!';
- except
- ..? 'except..'; # HIT
- ..? @ & '..'; # HIT
- finally
- ..? 'finally..'; # HIT
- end try;
- ?. 'DONE';
-
- ########################################
- # 1. "@" is a built-in variable in "except"
- # @ <- exception
- # 2. "raise" can throw an exception
- # exception can be anything
- ########################################
try..DONE
try..finally..DONE
try..finally..DONE
try..except..Floating point division by zero..finally..DONE
try..except..Stop!..finally..DONE
Download this DEMO
2. Variables: Dynamic typing in imperative programming
Fun is an imperative language which can change the program state via change the values of variables. Variables are dynamic typing and can convert types automatically when needed. Values can be changed within statements only and can be gotten in any expressions or statements in the scope.
2.1 Name
In FUN, the valid names are /[$_@A-Za-z][$_@A-Za-z0-9]*/ (Regular Expression) limited. In fact, all identifiers (IDs) too.
There are 40 reserved keywords cannot be used by programmers:
if then elsif else end case is when do
loop exit next while for to step in
try except finally raise fun return
class atom new var use as null nil
div mod not and or xor bit true false
There are 4 built-in variables in FUN:
@ result this base
@ is
built in "case", "except", "fun" and etc.,
result is
built in "fun",
this/base is
built in "class".
2.2 Type
Fun's variables have the following types: base types(number, string, bool, time), object types(set, object, class, function) and others.
All base types are value types. All object types are reference types, support shallow copy only.
- ########################################
- # type
- ########################################
- # Number: [0-9]+(\.[0-9]+)?(e-?[0-9]+)?|0x[0-9a-f]+|0b[01]+
- # String: Quoted with ' or " or `
- # Time: @String
- # Bool: true/false
- # Null: null/nil
- ########################################
-
- # Number
- ?. 123456789;
- var n = 1.23456789; ?. n;
- n = 0xffffffff; ?. n;
-
- # String
- ?. "This is a string.";
- var s = 'Hello, string.'; ?. s;
- ?. 'it''s me'; // '...''...', "...""...", `...``...`, `...`id`...` (for quine)
-
- # Time
- ?. @"2010-04-09 15:35:15";
- var t = @'2010-04-09 15:35:15'; ?. t;
-
- # Bool
- ?. true;
- var b = false; ?. b;
-
- # Null
- ?. '[' & null & ']';
123456789
1.23456789
4294967295
This is a string.
Hello, string.
it's me
2010-4-9 15:35:15
2010-4-9 15:35:15
True
False
[]
Download this DEMO
2.3 Calculation
Variables can be combined to expressions via calculation. There are some types of expressions and calculations: number, string, bool and etc. The bool expressions can be used as conditions in control flow.
- ########################################
- # calc
- ########################################
- # Number: + - * / div mod ^
- # Bits: << >> (bit not/and/or/xor)
- # String: &
- # Time: + -
- # Bool: not and or xor
- # Regex: =~ !~
- # Set: in
- # Others: = !=(<>) < <= > >=
- # Set/Others for any types
- ########################################
- # precedence (default left association)
- ########################################
- # () [] .
- # ^
- # * / div mod
- # (bit not) -> right
- # (bit and/or/xor) << >>
- # + -
- # &
- # = != <> < <= > >= in =~ !~ -> none
- # not -> right
- # and
- # or xor
- ########################################
- # short-circuit calculation
- ########################################
- # and
- # or
- ########################################
-
- # Number
- var n1 = 9;
- var n2 = 3.3;
- ?. n1 + n2;
- ?. n1 - n2;
- ?. n1 * n2;
- ?. n1 / n2;
- ?. n1 div "4"; # Type conversions automatically
- ?. n1 mod '4'; # Type conversions automatically
- ?. n1 ^ 3; # power
-
- # Bits
- n1 = 0x0f;
- n2 = 0xf0;
- ?. n1 << 4; # shift left
- ?. n2 >> 4; # shift right
- ?. bit not n1;
- ?. n1 bit and n2;
- ?. n1 bit or n2;
- ?. n1 bit xor n2;
-
- # String
- var s1 = 'Hello';
- var s2 = ", world!";
- ?. s1 & s2 & ' - ' & 10000; # Type conversions automatically
-
- # Time
- var t1 = @"2010-04-09 15:35:15";
- var t2 = @'2010-03-09 15:35:15';
- ?. t1 + 100;
- ?. t1 - t2;
-
- # Bool
- var b1 = true;
- var b2 = false;
- ?. not not b1;
- ?. not (b1 and b2);
- ?. b1 or b2;
- ?. b1 xor b2;
-
- # Set
- ?. "Hello" in ["Hello", "World"];
-
- # Others - Compare
- ?. not 1 = 2;
- ?. 1 != 2;
- ?. 1 <> 2;
- ?. 1 < 2;
- ?. 1 <= 2;
- ?. not 1 > 2;
- ?. not 1 >= 2;
-
- # Null - All null values equal null (number, string, bool, etc.)
- ?. null = 0;
- ?. null = false;
- ?. null = '';
-
- # Short-circuit calculation
- ?. true or 1 = 0; # "1 = 0" doesn't need calc
- ?. false and 1 = 1; # "1 = 1" doesn't need calc
-
- # Optional
- ?. Undefined?;
- ?. Undefined?.Undefined?;
12.3
5.7
29.7
2.72727272727273
2
1
729
240
15
4294967280
0
255
255
Hello, world! - 10000
2010-7-18 15:35:15
31
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
False
Download this DEMO
2.4 Assignment
Values of variables can be assigned in statements only.
- ########################################
- # assign
- ########################################
- # v = expression;
- # v += expression;
- # v -= expression;
- # v *= expression;
- # v &= expression;
- ########################################
-
- var a = 1; ?. a = 1;
- a += 2; ?. a = 3;
- a -= 1; ?. a = 2;
- a *= 2; ?. a = 4;
- a &= 5; ?. a = '45';
True
True
True
True
True
Download this DEMO
2.5 Scope
Variables are defined by "var" in a block. A block means a module (file) or any control flow block such as if/elsif/else/case/when/loop or fun/class and etc. Variables accessing always for the nearest block, if not found then for the upper level.
- ########################################
- # scope
- ########################################
-
- var a = 1;
- var b = 2;
- ?. a = 1;
- ?. b = 2;
-
- if true then
- ..var a = 2; # new "a"
- ..b = 22; # old "b"
- ..?. a = 2;
- ..?. b = 22;
-
- ..if true then
- ....var a = 3; # new "a"
- ....b = 33; # old "b"
- ....?. a = 3;
- ....?. b = 33;
- ..end if;
-
- ..?. a = 2;
- ..?. b = 33;
- end if;
-
- ?. a = 1;
- ?. b = 33;
True
True
True
True
True
True
True
True
True
True
Download this DEMO
3. Functions: Procedural and modular programming
Fun supports general procedural programming. Functions in FUN can be defined in any block and the rules of scope are same as scope of variables.
3.1 Functions define and call
- ########################################
- # fun
- ########################################
- # fun name ( parameters )
- # cmds
- # end fun;
- ########################################
-
- # Define a function named "foo"
- fun foo()
- ..?. 'Hello, fun!';
- end fun;
-
- # Call function "foo"
- foo();
-
- # Define a function with parameters
- fun max(a, b)
- ..if a > b then
- ....result = a;
- ..else
- ....result = b;
- ..end if;
- end fun;
-
- fun min(a, b)
- ..if a < b then
- ....return a;
- ..else
- ....return b;
- ..end if;
- end fun;
-
- ?. max(1, 9);
- ?. max(9, 1);
- ?. min(1, 9);
- ?. min(9, 1);
-
- ########################################
- # 1. "result", "@" are built-in variables in "fun"
- # they all be defined as the result value of the fun
- # 2. "return" means return the fun
- # it can return with/without a value
- # 3. "exit" can exit a fun too
- ########################################
Hello, fun!
9
9
1
1
Download this DEMO
3.2 Default arguments
- ########################################
- # fun-default
- ########################################
-
- # test define
- fun test(a, b, c)
- ..# embed inner fun
- ..fun echo(name, str)
- ....? name & '=[' & str & '] ';
- ..end fun;
-
- ..# echo the parameters
- ..echo('a', a);
- ..echo('b', b);
- ..echo('c', c);
- ..?. 'DONE';
- end fun;
-
- test(1, 2, 3);
- test(1, 2); # keep param(s) 'c' as null
- test(1); # keep param(s) 'b' and 'c' as null
-
- ########################################
- # 1. Default arguments always be set as null
- ########################################
a=[1] b=[2] c=[3] DONE
a=[1] b=[2] c=[] DONE
a=[1] b=[] c=[] DONE
Download this DEMO
3.3 Named parameters
- ########################################
- # fun-named
- ########################################
- # { name: value } 0..n
- ########################################
-
- # test define
- fun test(a, b, c)
- ..# embed inner fun
- ..fun echo(name, str)
- ....? name & '=[' & str & '] ';
- ..end fun;
-
- ..# echo the parameters
- ..echo('a', a);
- ..echo('b', b);
- ..echo('c', c);
- ..?. 'DONE';
- end fun;
-
- test(a: 1, b: 2, c: 3);
- test(c: 1, b: 2, a: 3);
- test(c: 1, b: 2); # keep param(s) 'a' as null
- test(c: 1); # keep param(s) 'b' and 'a' as null
-
- ########################################
- # 1. Default arguments always be set as null
- ########################################
a=[1] b=[2] c=[3] DONE
a=[3] b=[2] c=[1] DONE
a=[] b=[2] c=[1] DONE
a=[] b=[] c=[1] DONE
Download this DEMO
3.4 Call by reference
Fun supports call by value (default) and call by reference. You could call by reference when call a function with "var" arguments.
All object types (reference types) are passed pointers, not call by reference.
- ########################################
- # fun-ref
- ########################################
-
- fun swap(a, b)
- ..var t = a;
- ..a = b;
- ..b = t;
- end fun;
-
- var i = 1;
- var j = 9;
- ?. i & ' ' & j;
-
- swap(i, j); # Call by value
- ?. i & ' ' & j;
-
- swap(var i, var j); # Call by reference
- ?. i & ' ' & j;
-
- swap(var i, var j); # Call by reference
- ?. i & ' ' & j;
-
- ########################################
- # 1. pass "var" arguments to call by reference
- ########################################
1 9
1 9
9 1
1 9
Download this DEMO
3.5 Recursions
- ########################################
- # fun-recursion
- ########################################
-
- for i = 1 to 17 do
- ..?. fact(i);
-
- ..fun fact(n)
- ....if n > 1 then
- ......result = n * fact(n - 1); # call fact recursion
- ....else
- ......result = 1;
- ....end if;
- ..end fun;
- end do;
1
2
6
24
120
720
5040
40320
362880
3628800
39916800
479001600
6227020800
87178291200
1307674368000
20922789888000
355687428096000
Download this DEMO
3.6 Uses
Fun supports reuse resources via "use".
- ########################################
- # use
- ########################################
- # use string;
- ########################################
- # use string as id;
- ########################################
-
- ?. '----Use fun-ref.fun ...';
-
- use 'fun-ref.fun';
-
- ?. '----Used----';
-
- i = 11;
- j = 99;
- ?. i & ' ' & j;
-
- swap(i, j); # Call by value
- ?. i & ' ' & j;
-
- swap(var i, var j); # Call by reference
- ?. i & ' ' & j;
-
- swap(var i, var j); # Call by reference
- ?. i & ' ' & j;
-
- use 'fun-ref.fun' as lib; # use as alias
-
- i = 111;
- j = 999;
- ?. i & ' ' & j;
-
- lib.swap(var i, var j); # use via module_alias.name
- ?. i & ' ' & j;
-
- ########################################
- # 1. The used file will be inited at first (Run Once)
- # 2. Can access the IDs for top level of the used file
- # Variables and functions/classes and so on
- # 3. The latest IDs are valid for same name
- # Multiple files be used
- # 4. Can re-name the module as a new id, such as
- # use "..." as newone;
- # ?. newone.test();
- # 5. Files Can be used in anywhere
- ########################################
----Use fun-ref.fun ...
1 9
1 9
9 1
1 9
----Used----
11 99
11 99
99 11
11 99
111 999
999 111
Download this DEMO
4. Classes: Class-based object-oriented programming
Fun is an object-oriented programming language. It supports class-based single inheritance and polymorphism.
4.1 Classes define and objects create
Fun uses "class" keyword to define classes. Create an instance of class via call the constructor of the class.
- ########################################
- # class
- ########################################
- # class name ( parameters )
- # cmds
- # end class;
- ########################################
-
- # define a class
- class CA()
- ..# test for class constructor
- ..? 'CA-constructor-starting..';
-
- ..# member variable
- ..var name = 'CA';
-
- ..# member function
- ..fun showName()
- ....?. name;
- ..end fun;
-
- ..fun getName()
- ....return name;
- ..end fun;
-
- ..?. 'DONE';
- end class;
-
- # create a class instance (object)
- var a = CA();
-
- # use the member variable of object
- ?. a.name;
-
- # use the member function of object
- a.showName();
- ?. a.getName();
CA-constructor-starting..DONE
CA
CA
CA
Download this DEMO
4.2 Inheritance and polymorphism
In FUN, subclasses are inherited from single base class. When an object was created, constructor of super class was called at first automatically. All same IDs (Variables and functions and etc.) will override from base classes.
- ########################################
- # class-subclass
- ########################################
- # class name = base ( parameters )
- # cmds
- # end class;
- ########################################
-
- # define a class
- class CA()
- ..? 'CA-constructor-starting..';
-
- ..var name = 'CA';
-
- ..fun showName()
- ....?. name;
- ..end fun;
-
- ..fun getName()
- ....return name;
- ..end fun;
-
- ..?. 'DONE';
- end class;
-
- # define a sub-class
- class CB = CA()
- ..? 'CB-constructor-starting..';
-
- ..name = 'CB';
-
- ..?. 'DONE';
- end class;
-
- class CC = CB()
- ..? 'CC-constructor-starting..';
-
- ..name = 'CC';
-
- ..fun showName()
- ....?. '--CC.showName() .. ' & name & ' .. DONE';
- ..end fun;
-
- ..?. 'DONE';
- end class;
-
- fun test(o)
- ..?. o.name;
- ..o.showName();
- ..?. o.getName();
- end fun;
-
- ?. '--CA--';
- test( CA() );
-
- ?. '--CB--';
- test( CB() );
-
- ?. '--CC--';
- test( CC() );
--CA--
CA-constructor-starting..DONE
CA
CA
CA
--CB--
CA-constructor-starting..DONE
CB-constructor-starting..DONE
CB
CB
CB
--CC--
CA-constructor-starting..DONE
CB-constructor-starting..DONE
CC-constructor-starting..DONE
CC
--CC.showName() .. CC .. DONE
CC
Download this DEMO
4.3 This and base
- ########################################
- # class-base
- ########################################
- # class name = base ( parameters )
- # cmds
- # end class;
- ########################################
- # this.field or this.method()
- # base.field or base.method()
- ########################################
-
- // base class
- class CBas()
- ..var field = 'field';
-
- ..fun method()
- ....?. field;
- ..end fun;
-
- ..fun test()
- ....?. field;
- ....?. this.field;
- ....method(); // call the method in this class
- ....this.method(); // call the override method
- ..end fun;
- end class;
-
- // sub class
- class CSub = CBas()
- ..fun method()
- ....? 'CSub.method()..';
- ....base.method(); // call the method in base classes
- ..end fun;
- end class;
-
- // test ...
- var bas = CBas();
- var sub = CSub();
- ?. '--test--bas--'; ?. bas.field; bas.test();
- ?. '--test--sub--'; ?. sub.field; sub.test();
- // test ...
- bas.field = 'bas';
- sub.field = 'sub';
- ?. '--test--bas--'; ?. bas.field; bas.test();
- ?. '--test--sub--'; ?. sub.field; sub.test();
--test--bas--
field
field
field
field
field
--test--sub--
field
field
field
field
CSub.method()..field
--test--bas--
bas
bas
bas
bas
bas
--test--sub--
sub
sub
sub
sub
CSub.method()..sub
Download this DEMO
4.4 Override operators
- ########################################
- # class-operators-override
- ########################################
- # this['+'] = fun (right) { ... }
- # this['-'] = fun (right) { ... }
- # ...
- ########################################
-
- class Number(me)
- ..this['+'] = fun (you) {
- ....return me + you;
- ..};
-
- ..this['-'] = fun (you) {
- ....return me - you;
- ..};
-
- ..this['*'] = fun (you) {
- ....return me * you;
- ..};
-
- ..this['/'] = fun (you) {
- ....return me / you;
- ..};
-
- ..this['\'] = fun (you) {
- ....return me div you;
- ..};
-
- ..this['%'] = fun (you) {
- ....return me mod you;
- ..};
-
- ..this['^'] = fun (you) {
- ....return me ^ you;
- ..};
-
- ..this['='] = fun (you) {
- ....return me = you;
- ..};
-
- ..this['!'] = fun (you) {
- ....return me <> you;
- ..};
-
- ..this['>'] = fun (you) {
- ....return me > you;
- ..};
-
- ..this['<'] = fun (you) {
- ....return me < you;
- ..};
- end class;
-
- var n = Number(10);
- ?. n .+ 2; // .+ means + override version
- ?. n .- 3; // .- means - override version
- ?. n .* 4; // .* means * override version
- ?. n ./ 5; // ./ means / override version
- ?. n .\ 6; // .\ means \ override version
- ?. n .% 7; // .% means % override version
- ?. n .^ 8; // .^ means ^ override version
- ?. n .> 9; // .> means > override version
- ?. n .= 10; // .= means = override version
- ?. n .< 11; // .< means < override version
- ?. n .! 12; // .! means ! override version
-
- ########################################
- # 1. Currently FUN supports override operators(16):
- # + - * / \ % ^ ! = < > & ~ ? | #
- # 2. All override operators same precedence
- # 3. All override operators are duality operators
- # So the functions will have one parameter
- # 4. Must use "." append operators to use them
- ########################################
12
7
40
2
1
3
100000000
True
True
True
True
Download this DEMO
4.5 Anything is object
Anything include number, string, set or list, function or class and etc is object.
- ########################################
- # object
- ########################################
-
- # number
- ?. (1.atan() * 2).sin();
-
- # string
- ?. '123'.length();
- ?. 'abc'.upper();
- ?. 'ABC'.lower();
-
- # set/list
- ?. [a: 1, b: 2, c: 3].a;
-
- var s1 = [
- ..f1: fun(){
- ....?. 'hello, f1';
- ..},
- ..f2: fun(){
- ....?. 'hello, f2';
- ..}
- ];
-
- s1.f1();
- s1.f2();
1
3
ABC
abc
1
hello, f1
hello, f2
Download this DEMO
5. Functional programming
Fun is a functional programming language. Fun supports first-class functions and higher-order functions. Fun supports anonymous functions, closures and currying.
5.1 First-class functions and higher-order functions
- ########################################
- # functional
- ########################################
-
- fun foo(a)
- ..?. 'Hello, ' & a & '!';
- end fun;
-
- var f = foo;
- f('functional');
-
- fun high(f, a)
- ..f(a);
- end fun;
-
- var h = high;
- h(f, 'functional');
Hello, functional!
Hello, functional!
Download this DEMO
5.2 Anonymous functions
- ########################################
- # functional-anonymous
- ########################################
- # fun ( parameters ) { cmds }
- ########################################
- # fun ( ) { cmds }
- ########################################
- # ( ) { cmds }
- ########################################
- # { cmds }
- ########################################
-
- fun If(a, b, c)
- ..if a then
- ....return b();
- ..elsif c <> null then
- ....return c();
- ..end if;
- end fun;
-
- fun Loop(a, b)
- ..while a() do
- ....b();
- ..end do;
- end fun;
-
- fun For(init, cond, nex1, code)
- ..init();
- ..while cond() do
- ....code();
- ....nex1();
- ..end do;
- end fun;
-
- fun testIf()
- ..If(
- ....true,
- ....{?.true} # Anonymous function (Lambda calculus)
- ..);
-
- ..If(
- ....false,
- ....{?.false}, # Anonymous function (Lambda calculus)
- ....{?.true} # Anonymous function (Lambda calculus)
- ..);
- end fun;
- testIf();
-
- fun testLoop()
- ..var i = 0;
- ..var s = 0;
-
- ..Loop(
- ....{return i < 10}, # Anonymous function (Lambda calculus)
- ....{?, i; i += 1} # Anonymous function (Lambda calculus)
- ..);
- ..?. 'DONE';
-
- ..For(
- ....{i = 1; s = 0}, # Anonymous function (Lambda calculus)
- ....{@ = i <= 1000}, # Anonymous function (Lambda calculus)
- ....{i += 1}, # Anonymous function (Lambda calculus)
- ....{s += i} # Anonymous function (Lambda calculus)
- ..);
- ..?. s;
- end fun;
- testLoop();
-
- ########################################
- # 1. "@" is a built-in variable in "fun"
- # @ <- result
- # 2. {...} <- (){} <- fun(){}
- # with parameters must use
- # fun(parameters){} or
- # (parameters){}
- # 3. "?." means output (append extra \r\n)
- # "?," means output (append a blank' ')
- # "?:" means output (append a TAB )
- # "?" means output (without any extra)
- ########################################
True
True
0 1 2 3 4 5 6 7 8 9 DONE
500500
Download this DEMO
5.3 Closures
- ########################################
- # functional-closure
- ########################################
-
- fun counter()
- ..var i = 0;
- ..return fun(){
- ....i += 1;
- ....result = i;
- ..};
- end fun;
-
- var c = counter();
- ?. c();
- ?. c();
- ?. c();
-
- fun mul(bas)
- ..return fun(times){
- ....return bas * times;
- ..};
- end fun;
-
- var m = mul(3);
- ?. m(4);
- ?. m(5);
- ?. m(6);
1
2
3
12
15
18
Download this DEMO
5.4 Currying
- ########################################
- # functional-currying
- ########################################
- # fun name ( arguments )
- ########################################
-
- fun mul(bas, times)
- ..return bas * times;
- end fun;
-
- var m = fun mul(3);
- ?. m(4);
- ?. m(5);
- ?. m(6);
12
15
18
Download this DEMO
5.5 Mathematics functions
- ########################################
- # functional-mathematics
- ########################################
- # var name ( arguments ) = exp;
- ########################################
- # ( arguments ) -> exp
- # ( ) -> exp
- # argument -> exp
- # -> exp
- ########################################
-
- var ifv = (condition, vtrue, vfalse){
- ..if condition then
- ....return vtrue;
- ..else
- ....return vfalse;
- ..end if;
- };
-
- var forto = (start, end1, step1, action){
- ..result = 0;
- ..for i = start to end1 step step1 do
- ....result = action(i, result);
- ..end do;
- };
-
- var sqrt(n) = n ^ 0.5;
-
- var max(a, b) = ifv(a > b, a, b);
- var min(a, b) = ifv(a < b, a, b);
-
- var sqrt2 = n -> n ^ 0.5;
- var max2 = (a, b) -> ifv(a > b, a, b);
- var min2 = (a, b) -> ifv(a < b, a, b);
-
- var sum(s, e, i) = forto(s, e, i, (i, r) -> r + i);
- var sum1(n) = sum(1, n, 1);
- var fact(n) = forto(1, n, 1, (i, r) -> ifv(r=0, 1, r) * i);
-
- ?. sqrt(9);
- ?. max(1, 2);
- ?. min(1, 2);
- ?. sqrt2(9);
- ?. max2(1, 2);
- ?. min2(1, 2);
- ?. sum(1, 10000, 1);
- ?. sum1(10000);
- ?. fact(17);
3
2
1
3
2
1
50005000
50005000
355687428096000
Download this DEMO
6. Sets: List process
Fun provides a simple and flexible list. It looks like an array or associative array.
6.1 Arrays
- ########################################
- # set
- ########################################
- # [ value, value, value ... ]
- ########################################
-
- var list = [0, 1, 2, 3, 4, 5];
-
- ?. 1 in list;
- ?. 3 in list;
- ?. 5 in list;
- ?. not 7 in list;
-
- for i in list do
- ..?, i;
- end do;
- ?. 'DONE';
-
- ?. list[0];
- ?. list[1];
- ?. list[-1];
- ?. list[-2];
-
- list[3] = -3;
- list[-1] = -5;
-
- for i in list do
- ..?, i;
- end do;
- ?. 'DONE';
-
- ########################################
- # 1. The elements can be anything includes base types and object types
- #
- # [num: 1, str: "string", time: @'2010-04-23 17:26:22', bool: false]
- #
- # [[name: 'fun', value: 'function'], [name: 'obj', value: 'object']]
- #
- # 2. Index of list start from 0 to N-1 and from -N to -1, as below:
- #
- # var N = list.count();
- #
- # [ 0 1 2 3 ... N-3 N-2 N-1 ]
- # ------------> ++1 --------->
- #
- # [ 0-N 1-N 2-N 3-N ... -3 -2 -1 ]
- # <------------ --1 <---------
- #
- # !!! Index must be Integer, Don't Use any Float or Double Index !!!
- ########################################
True
True
True
True
0 1 2 3 4 5 DONE
0
1
5
4
0 1 2 -3 4 -5 DONE
Download this DEMO
6.2 Mappings: Associative arrays
In FUN, arrays and associative arrays (hash tables, dictionaries, mappings) are same. You could set an element as a name or not.
- ########################################
- # set-named
- ########################################
- # [ name: value, name: value ... ]
- ########################################
-
- var list = [red: 0x0000ff, green: 0x00ff00, blue: 0xff0000, 0xffff00, 'ɫ': 0xffff00, 'ʾ': a -> a & '!', ];
-
- for i in list do
- ..?, i;
- end do;
- ?. 'DONE';
-
- ?, 'red';
- ?, list[0];
- ?, list['red'];
- ?. list.red;
-
- ?, 'green';
- ?, list[1];
- ?, list['green'];
- ?. list.green;
-
- ?, 'blue';
- ?, list[2];
- ?, list['blue'];
- ?. list.blue;
-
- list.yellow = 0x00ffff;
- list['white'] = 0xffffff;
- list["black"] = 0x000000;
-
- for i in list do
- ..?, i;
- end do;
- ?. 'DONE';
-
- ?. list.'ɫ';
- ?. list.'ʾ'('ͷ');
255 65280 16711680 16776960 16776960 DONE
red 255 255 255
green 65280 65280 65280
blue 16711680 16711680 16711680
255 65280 16711680 16776960 16776960 65535 16777215 0 DONE
16776960
ͷ!
Download this DEMO
6.3 New set
All set/list defines are constants similar to classes. Only one instance created by compile time. You could get a new set or list instance with "new" keyword or @clone() method.
- ########################################
- # set-clone
- ########################################
- # new [...]
- ########################################
- # s.@clone()
- ########################################
-
- var s1 = new [1, 2, 3];
- s1.@each((i){?,i}); ?. null;
-
- var s2 = s1.@clone();
- s2.@each((i){?,i}); ?. null;
-
- s1[0] = 11;
- s2[1] = 22;
- s1.@each((i){?,i}); ?. null;
- s2.@each((i){?,i}); ?. null;
1 2 3
1 2 3
11 2 3
1 22 3
Download this DEMO
6.4 Library for Set/List
- ########################################
- # lib-set
- ########################################
- # built-in (4)
- ########################################
- # s.@count ()
- # s.@each (fun, reverse = false) # fun (val) or (val, key)
- # s.@add (v1, v2, ...)
- # s.@clone ()
- ########################################
-
- var pathSet(set, path, value) = locate(set, path, value, 1);
- var pathInc(set, path, value) = locate(set, path, value, 2);
- fun locate(set, path, value, mode) // mode: 0-get, 1-set, 2-inc
- ..var lp = nil;
- ..for p in path do
- ....if lp <> nil then
- ......if set[lp] = nil then
- ........set[lp] = new [];
- ......end if;
- ......set = set[lp];
- ....end if;
- ....lp = p;
- ..end do;
- ..result = set[lp];
- ..if mode = 1 then
- ....set[lp] = value;
- ..elsif mode = 2 then
- ....set[lp] += value;
- ..end if;
- end fun;
-
- fun filter(s, f, n)
- ..var ret = n or new [];
- ..s.@each((v, k){
- ....if f(var v, k) then
- ......if k <> nil then
- ........ret[k] = v;
- ......else
- ........ret.@add(v);
- ......end if;
- ....end if;
- ..});
- ..result = ret;
- end fun;
-
- fun find(s, f)
- ..result = nil;
- ..for v in s do
- ....if f(v) then
- ......return v;
- ....end if;
- ..end do;
- end fun;
-
- fun compose(args, a)
- ..args.@each((f){
- ....a = f(a);
- ..}, true);
- ..return a;
- end fun;
-
- fun map(fn, list)
- ..var set = new [];
- ..list.@each( v -> set.@add(fn(v)) );
- ..result = set;
- end fun;
-
- fun fromPairs(list)
- ..var set = new [];
- ..list.@each( (v){
- ....set[v[0]] = v[1];
- ..});
- ..result = set;
- end fun;
-
- fun reducePairs(p, a)
- ..a = a or new [];
- ..for i = 0 to p.@count() -1 step 2 do
- ....a[p[i]] = p[i+1];
- ..end do;
- ..result = a;
- end fun;
-
- fun mapReduce(map, reduce, a)
- ..var ret;
- ..a.@each( v -> reduce(map(v), var ret) );
- ..result = ret;
- end fun;
- var mapReduceEx(map, reduce, preMap, a) = mapReduce(map, reduce, preMap(a));
-
- fun formats(str, set)
- ..result = str.replace(/\{(\w++)\}/g, (m){
- ....result = formats(set[m.@(1)], set);
- ..});
- end fun;
-
- var mergeJson = mergeSet;
- fun mergeSet(src, dst, isInc)
- ..for k: v in src do
- ....if k = nil then
- ......dst.@add(v);
- ....elsif dst[k] = nil then
- ......dst[k] = v;
- ....else
- ......try
- ........if v.@count() > 0 or dst[k].@count() > 0 then
- ..........mergeSet(v, dst[k], isInc);
- ........else
- ..........setRet();
- ........end if;
- ......except //?. @;
- ........setRet();
- ......end try;
- ....end if;
-
- ....fun setRet()
- ......try
- ........if isInc then
- ..........dst[k] += v * 1;
- ........else
- ..........dst[k] = v;
- ........end if;
- ......except
- ........dst[k] = v;
- ......end try;
- ....end fun;
- ..end do;
- end fun;
-
- # result = s2 - s1
- fun diffSet(s1, s2, ret)
- ..ret = ret or new [];
- ..result = ret;
-
- ..for k: v in s2 do
- ....if s1[k] = nil then
- ......setRet();
- ....else
- ......try
- ........if s2[k].@count() >= 0 and s1[k].@count() >= 0 then
- ..........var d = diffSet(s1[k], s2[k]);
- ..........if d then
- ............ret[k] = d;
- ..........end if;
- ........else
- ..........setRet();
- ........end if;
- ......except //?. @;
- ........setRet();
- ......end try;
- ....end if;
-
- ....fun setRet()
- ......if v <> s1[k] then
- ........if k = nil then
- ..........ret.@add(v);
- ........else
- ..........ret[k] = v;
- ........end if;
- ......end if;
- ....end fun;
- ..end do;
- end fun;
Download this LIB
- ########################################
- # test for lib-set.fun
- ########################################
-
- use '..\lib\lib-set.fun';
-
- test();
-
- fun test()
- ..?. [1, 2, 3].@count();
-
- ..[1, 2, 3].@each((i){?,i});
- ..?. 'DONE';
-
- ..var abc = [a: 1, b: 2, c: 3];
- ..abc.@each((i, k){?,k & '=' & i});
- ..?. 'DONE';
-
- ..abc.@add(4, 5, 6, g: 7, h: 8, i: 9)
- ......@each((i, k){?,k & '=' & i});
- ..?. 'DONE';
- end fun;
3
1 2 3 DONE
a=1 b=2 c=3 DONE
a=1 b=2 c=3 =4 =5 =6 g=7 h=8 i=9 DONE
Download this DEMO
6.5 JSON for data-interchange
- ########################################
- # lib-json
- ########################################
- # built-in (2)
- ########################################
- # set.@toJson (format, json) # save set as JSON string
- # str.getJson (json = false) # load str as JSON set/list/tree
- ########################################
- # 1. format param of @toJson()
- # default 0, format levels
- # join (concat) if format = -1
- # 2. json param of @toJson()
- # default false, name quoted as "name"
- ########################################
-
- fun JSON()
- ..fun Parse(s)
- ....result = s.getJson(json: true);
- ..end fun;
-
- ..fun Stringify(s, level)
- ....result = s.@toJson(level, json: true);
- ..end fun;
- end fun;
Download this LIB
- ########################################
- # test for lib-json.fun
- ########################################
-
- use '..\lib\lib-json.fun';
-
- test();
-
- fun test()
- ..var s1 = '[1, 2, 3]'.getJson();
- ..s1.@each((i){?,i});
- ..?. '';
- ..?. s1.@toJson();
-
- ..var s2 = "[{key: str, val: 'this is a string.'},
- {key: num, val: 123456789}]".getJson();
- ..s2.@each(
- ....(i){
- ......i.@each(
- ........(v, k){
- ..........?, k & ': ' & v;
- ........}
- ......);
- ......?. null;
- ....}
- ..);
- ..?. 'format: 0';
- ..?. s2.@toJson();
- ..?. 'format: 1 level';
- ..?. s2.@toJson(format: 1);
- ..?. 'format: 2 levels';
- ..?. s2.@toJson(format: 2);
- end fun;
1 2 3
[1,2,3]
key: str val: this is a string.
key: num val: 123456789
format: 0
[{key:"str",val:"this is a string."},{key:"num",val:123456789}]
format: 1 level
[
{key:"str",val:"this is a string."},
{key:"num",val:123456789}
]
format: 2 levels
[
{
key: "str",
val: "this is a string."
},
{
key: "num",
val: 123456789
}
]
Download this DEMO
7. Regular expressions
Fun provides a PCRE regular expressions handling engine. The syntax of RE is Perl-Compatible.
7.1 Syntax
A RE (Regular Expression) pattern looks like /[^\/\s]+/[gimsux]*, %[^\%\s]+%[gimsux]* and $("[^"]+"|'[^']+'|`[^`]+`)[gimsux]* (RE). You could also convert a string to RE pattern via using string.toRegex(options).
Fun supports built-in functions match() and replace(), such as str.match(re) or re.match(str).
Operators of RE: =~ (match) and !~ (not match).
7.2 RE library (match and replace)
- ########################################
- # lib-regex
- ########################################
- # built-in (3)
- ########################################
- # match:
- # str.match(str) # match only in string form
- #
- # str.match(reg) # match in regex pattern
- # reg.match(str) # match in regex pattern
- # str.match(reg, action) # match in regex pattern
- # reg.match(str, action) # match in regex pattern
- #
- # replace:
- # str.replace(str, newstr) # replace all in string form
- #
- # str.replace(reg, newone) # replace in regex pattern
- # reg.replace(str, newone) # replace in regex pattern
- #
- # toRegex:
- # str.toRegex(options) # options: g, i, m, s, u, x
- ########################################
- # 1. str: string
- # reg: regex
- # 2. if 'g' in regex option, match() return a list of matched string
- # if action exists, the action will be performed with a match
- # object argument for every match, and return string concated
- # from the return value of the action
- # or, match() return a match object with all matched groups
- # (matched string is 0 of the groups)
- # if action exists, the action will be performed with the match
- # object one time, and return string from the return value of
- # the action
- # 3. if newone is string, replace with the string one time or all
- # (according to 'g')
- # or if newone is action (function), replace with the return value
- # of the action
- # 4. options:
- # g - global, match or replace all
- # i - ignore case, case insensitive
- # m - multiple lines, ^ and $ match start or end of any line
- # s - single line, dot(.) match any character, even newline(\n)
- # u - UTF8
- # x - extended, allow regex to contain extra whitespace and comments
- # e.g.: '\d+'.toRegex('g')
- ########################################
-
- # return left string, the right argument must be var right, e.g.:
- # var right = '';
- # var left = splitonce(/\./, 'a.b', var right);
- # // Now, left = a, right = b
- var splitonce(re, str, right) = re.match(str, (m){
- ....@ = m.missed();
- ....right = m.rest();
- ..});
-
- # return a list
- var split = (re, str){
- ......result = new [];
- ......var m = re.match(str);
- ......while m.@@() <> nil loop
- ........result.@add(m.missed());
- ........m.match();
- ......end loop;
- ......result.@add(m.missed());
- };
Download this LIB
- ########################################
- # test for lib-regex.fun
- ########################################
-
- use '..\lib\lib-regex.fun';
-
- test();
-
- fun test()
- ..# str.match(str), return bool
- ..?, 'abcd123efg' =~ '123';
- ..?, 'abcd123efg' !~ '1234';
- ..?, 'abcd123efg' .match( '123' );
- ..?. not 'abcd123efg' .match( '1234' );
-
- ..# str.match(reg), reg.match(str), return bool
- ..?, not not 'abcd123efg' =~ /123/; # Convert to bool
- ..?, /1234/ !~ 'abcd123efg';
- ..?, not not 'abcd123efg' .match( /123/ );
- ..?. not '1234'.toRegex().match( 'abcd123efg' );
-
- ..# g, return list of matched string
- ..'abcd123efg'.match(/\d/g).@each((m){? '[' & m & ']'});
- ..?. null;
-
- ..# not g, return match object
- ..?. 'abcd123efg'.match(/\d+/).value();
-
- ..# str.match(reg, action), reg.match(str, action)
- ..# return string concated from return value of action
- ..?. 'abcd123efg'.match(/\d/g, (m){@= '[' & m.value() & ']'});
- ..?. 'abcd123efg'.match(/\d/i, (m){@= '[' & m.value() & ']'});
-
- ..# str.replace(str, newstr)
- ..?. 'abcd123efg'.replace('123', '[123]');
-
- ..# str.replace(reg, newstr), reg.replace(str, newstr)
- ..?. 'abcd123efg'.replace(/\d/g, '[$0]');
-
- ..# str.replace(reg, action), reg.replace(str, action)
- ..?. 'abcd123efg'.replace(/\d/g, (m){@= '[' & m.value() * 3 & ']'});
-
- ..var right = '';
- ..var left = splitonce(/\./, 'a.b', var right);
- ..?. 'left: [$left], right: [$right]'.eval();
-
- ..split('a.b.c.d', /\./).@each((m){?, '[' & m & ']'});
- ..?. null;
- end fun;
True True True True
True True True True
[1][2][3]
123
[1][2][3]
[1]
abcd[123]efg
abcd[1][2][3]efg
abcd[3][6][9]efg
left: [a], right: [b]
[a] [b] [c] [d]
Download this DEMO
7.3 Match object
A match object is returned by the match() function without 'g' option and without action. And all action for match() and replace() have a match object parameter.
- ########################################
- # lib-match (match object)
- ########################################
- # built-in (6)
- ########################################
- # m.match() # perform next match, return success or not
- #
- # m.missed() # return string missed (Left string of matched)
- # m.value() # return string matched
- # m.value(substi) # return string substitute
- # m.@@() # return string matched
- # m.@@(substi) # return string substitute
- # m.rest() # return string rest (All right string)
- #
- # m.gcount() # return group count
- #
- # m.groups(num) # return group by index
- # m.groups(str) # return group by name
- # m.@(num) # group by index
- # m.@(str) # group by name
- ########################################
Download this LIB
- ########################################
- # test for lib-match.fun
- ########################################
-
- use '..\lib\lib-match.fun';
-
- test();
-
- fun test()
- ..var m = 'a.b.c.d' =~ /\./;
- ..loop
- ....print(m.missed(), 'missed');
- ....print(m.value(), 'value');
- ....print(m.@@(), '@@');
- ....print(m.rest(), 'rest');
- ....?. null;
- ....exit when not m.match();
- ..end loop;
- ..print(m.missed(), 'missed');
- ..?. null;
-
- ..m = 'a.b.c' =~ /^(?P<first>\w)\.(\w)\.(\w)$/;
- ..print(m.gcount(), 'Count');
- ..print(m.groups('first'), 'first');
- ..?. null;
- ..for i = 0 to m.gcount() - 1 do
- ....print(m.@(i), i);
- ..end do;
- ..?. null;
-
- ..fun print(str, name)
- ....?, '$name: [$str]'.eval();
- ..end fun;
- end fun;
missed: [a] value: [.] @@: [.] rest: [b.c.d]
missed: [b] value: [.] @@: [.] rest: [c.d]
missed: [c] value: [.] @@: [.] rest: [d]
missed: [d]
Count: [4] first: [a]
0: [a.b.c] 1: [a] 2: [b] 3: [c]
Download this DEMO
- //Outer brackets: \((\d++(?:\.\d++)?|(?R))(?:[+\-*/](?1))++\)
- // Simplify: \((\d+(\.\d+)?|(?R))([+\-*/](?1))+\)
- //Inner brackets: ((\d++(?:\.\d++)?|(?3))(?:[+\-*/](?2))++)|(\((?1)\))
- // Simplify: ((\d+(\.\d+)?|(?5))([+\-*/](?2))+)|(\((?1)\))
- var re = $'
- (
- (\d++(?:\.\d++)? | (?3))
- (?: [+\-*/]
- (?2)
- )++
- )
- |
- ( \( (?1) \) )
- 'x;
-
- ?. re.match('(((1+2-3)*4/5)-((1+2)-((3*4)/((5+(6-7))*(8/9+10.1)/11.2))))').@@();
(((1+2-3)*4/5)-((1+2)-((3*4)/((5+(6-7))*(8/9+10.1)/11.2))))
Download this DEMO
8. Libraries
Fun provides a lightweight basic class library, includes math calculate, string and time process, Windows API and COM, set/list process and regular expression ...
8.1 Math
- ########################################
- # lib-base
- ########################################
-
- fun ifval (condition, vtrue, vfalse)
- ..if condition then
- ....return vtrue;
- ..else
- ....return vfalse;
- ..end if;
- end fun;
- var ifv = ifval;
- var ifb(c) = ifv(c, true, false);
- var iff(c, t, f) = ifv(c(), t, f);
- var iffa(c, t, f) = ifa(c(), t, f);
-
- fun ifa (condition, atrue, afalse)
- ..if condition then
- ....return atrue ();
- ..else
- ....return afalse();
- ..end if;
- end fun;
-
- fun forloop (start, end1, step1, action, set)
- ..result = set;
- ..for i = start to end1 step step1 do
- ....result = action(i, result) or result;
- ..end do;
- end fun;
- var forto = forloop;
- var forE(num, action, set) = forto(1, num, 1, action, set);
-
- var filter = (start, end1, step1, cond, action){
- ..result = [];
- ..forto(start, end1, step1, (i, ret){
- ....if cond(i, ret) then
- ......action(i, ret);
- ....end if;
- ....result = ret;
- ..}, result);
- };
- var filter1(s, e, c, a) = filter(s, e, 1, c, a);
-
- fun foreach (set1, action)
- ..for k: v in set1 do
- ....action(v, k);
- ..end do;
- end fun;
- var forin = foreach;
Download this LIB
- ########################################
- # lib-math
- ########################################
- # built-in (6)
- ########################################
- # a.exp () # e ^ a
- # a.log () # LogE(a)
- # a.sin ()
- # a.cos ()
- # a.atan ()
- # a.random () # 1.random(), 0.random(), a.random(), -1.random(seed = 0)
- ########################################
-
- #use 'lib-base.fun';
-
- var pow(a, b) = a ^ b;
- var sqrt(a) = a ^ 0.5;
-
- var exp(a) = a.exp();
- var log(a) = a.log();
- var log2(a) = a.log() / 2.log();
- var log10(a) = a.log() / 10.log();
-
- var sin(a) = a.sin();
- var cos(a) = a.cos();
- var tan(a) = a.sin() / a.cos();
- var atan(a) = a.atan();
- var asin(a) = atan(a / sqrt(1 - a ^ 2));
- var acos(a) = atan(a / sqrt(1 - a ^ 2) * -1) + 2 * atan(1);
-
- var pi() = 1.atan() * 4;
- var e() = 1.exp();
- var phi() = (5^0.5+1)/2;
-
- #var max(a, b) = ifv(a > b, a, b);
- #var min(a, b) = ifv(a < b, a, b);
- #var abs(a) = ifv(a < 0, a * -1, a);
- var max(a, b) = a > b and a or b;
- var min(a, b) = a < b and a or b;
- var abs(a) = a < 0 and a * -1 or a;
- //var round(a) = a div 1;
- var round(a, b) = a * 10^b div 1 / 10^b;
- var floor(a) = a div 1 > a and a div 1 - 1 or a div 1;
- var ceil(a) = a div 1 < a and a div 1 + 1 or a div 1;
-
- var gcd(x, y) = y and gcd(y, x mod y) or x;
- var lcm(x, y) = x * y / gcd(x, y);
-
- var random() = 1.random(); # return x in 0..1
- var randomize() = 0.random();
- var randomint(a) = a.random(); # return x in 0..a
Download this LIB
- ########################################
- # test for lib-math.fun
- ########################################
-
- use '..\lib\lib-math.fun';
-
- test();
-
- fun test()
- ..?, 8 = pow(2, 3);
- ..?, 2 = sqrt(4);
- ..?, e() = exp(1);
- ..?, 2 = log(exp(2));
- ..?, 3 = log2(pow(2, 3));
- ..?. 4 = log10(pow(10, 4));
-
- ..?, 0.000001 > abs(1 - sin(pi() / 6) * 2);
- ..?, 0.000001 > abs(1 - cos(pi() / 3) * 2);
- ..?, 0.000001 > abs(1 - tan(pi() / 4));
- ..?, 0.000001 > abs(pi() - atan(sqrt(3)) * 3);
- ..?, 0.000001 > abs(pi() - asin(sqrt(3) / 2) * 3);
- ..?. 0.000001 > abs(pi() - acos(sqrt(3) / 2) * 6);
-
- ..?, pi();
- ..?. e();
-
- ..?, 9 = max(1, 9);
- ..?, 1 = min(1, 9);
- ..?, 1 = abs(-1);
- ..?, 1 = round(1.4);
- ..?, 1 = round(1.4999);
- ..?. 2 = round(1.5);
-
- ..randomize();
- ..?, random() < 1;
- ..?, random() < 1;
- ..?, random() < 1;
- ..?, randomint(100) < 100;
- ..?, randomint(100) < 100;
- ..?. randomint(100) < 100;
- end fun;
True True True True True True
True True True True True True
3.14159265358979 2.71828182845905
True True True True True True
True True True True True True
Download this DEMO
8.2 String
- ########################################
- # lib-string
- ########################################
- # built-in (9)
- ########################################
- # a.length ()
- # a.lower ()
- # a.upper ()
- # a.subpos (sub)
- # a.substr (pos, len) # Default pos=0, len=MAX, can be negative
- # a.move (ptr) # ptr: int, copy to ptr
- # a.x (n) # Replicate n times, or reverse (n = -1)
- # # sort (n = -2)
- # a.escape () # \ abtvrnf'"?/\ \x HH \u HHHH
- # a.format (A1, A2, ... An) # %s
- # a.eval () # $id
- ########################################
- # 1. a.escape()
- # \a \b \t \v \r \n \f
- # \' \" \? \/ \\
- # \xHH \uHHHH
- ########################################
Download this LIB
- ########################################
- # test for lib-string.fun
- ########################################
-
- use '..\lib\lib-string.fun';
-
- test();
-
- fun test()
- ..var a = 'Www.FunLang.Org';
-
- ..?. a.length();
- ..?. a.lower();
- ..?. a.upper();
-
- ..var b = a.lower();
- ..?. b.subpos('fun');
- ..?. b.substr();
- ..?. b.substr(4);
- ..?. b.substr(4, 7);
- ..?. b.substr(len: 3);
- ..?. b.substr(4, -8);
- ..?. b.substr(-3);
-
- ..?. a.substr(4, 7).x(3);
-
- ..var c = '[Hello\t\\\r\n\?\x40]';
- ..?. c;
- ..?. c.escape();
-
- ..var d = 'Error: %s at %s/%s.';
- ..?. d;
- ..?. d.format('"a" not found', 10, 3);
-
- ..var name = 'David';
- ..var e = 'Hello, $name.';
- ..?. e;
- ..?. e.eval();
- end fun;
15
www.funlang.org
WWW.FUNLANG.ORG
4
www.funlang.org
funlang.org
funlang
www
funlang
org
FunLangFunLangFunLang
[Hello\t\\\r\n\?\x40]
[Hello \
?@]
Error: %s at %s/%s.
Error: "a" not found at 10/3.
Hello, $name.
Hello, David.
Download this DEMO
8.3 Date and time
- ########################################
- # lib-time
- ########################################
- # built-in (2)
- ########################################
- # a.time () # 0.time(), a.time()
- # a.format() # yyyy-mm-dd hh:nn:ss:zzz
- ########################################
-
- var date() = 1.time(); # return date & time
- var time() = 0.time(); # return time
- var ms = 24 * 60 * 60 * 1000;
-
- class tick()
- ..var t = time();
- ..var get = {
- ....result = (time() - t) * ms div 1;
- ....t = time();
- ..};
- ..var getStr = {
- ....result = get();
- ....if result < 100 then
- ......result = result div 1 & ' milliseconds';
- ....elsif result < 1000 * 60 then
- ......result = result div 100 / 10 & ' seconds';
- ....elsif result < 1000 * 60 * 60 then
- ......result = result / 1000 div 6 / 10 & ' minutes';
- ....else
- ......result = result / 60000 div 6 / 10 & ' hours';
- ....end if;
- ..};
- ..var show = (s, ms){
- ....if ms = 0 then ms = 1; end if;
- ....result = s & ': ' & get() / ms;
- ..};
- end class;
-
- fun diffDate(y, m, d, default, diff, now)
- ..now = now or 1.time();
- ..result = default;
- ..try
- ....var mm = 'mm';
- ....var dd = 'dd';
- ....if diff in ['m', 'y'] then
- ......dd = 15;
- ......d = 15;
- ....end if;
- ....if diff = 'y' then
- ......mm = 7;
- ......m = 7;
- ....end if;
- ....result = now.format('yyyy-$mm-$dd'.eval()).toTime() - '$y-$m-$d'.eval().replace(/[^\d\-]++/g, '').toTime();
- ....if diff = 'm' then
- ......result = result / 30.436875 div 1;
- ....elsif diff = 'y' then
- ......result = result / 365.2425 div 1;
- ....end if;
- ..except
- ..end try;
- end fun;
Download this LIB
- ########################################
- # test for lib-time.fun
- ########################################
-
- use '..\lib\lib-time.fun';
-
- test();
-
- fun test()
- ..?. date();
- ..?. time();
-
- ..var t = tick();
- ..var s = 0;
- ..for i = 1 to 1000000 do
- ....s += i;
- ..end do;
- ..?, s;
- ..?. t.get();
- end fun;
2023-8-26 14:46:58
14:46:58
500000500000 156
Download this DEMO
8.4 File
- ########################################
- # lib-file
- ########################################
- # built-in (8)
- ########################################
- # f.load ()
- # f.save (str, cp = 0, append = false) # cp 10056 - utf8 without bom
- # f.find (sub = false) # find directory if f endswith '\'
- # f.copy (f2, f3, ...)
- # f.move (f2) # delete f if f2 = null
- # f.time (flag) # flag: 0-CreationTime, 1-LastWriteTime
- # f.size ()
- # f.hash (mode) # mode: 0-MD5, 1-SHA1
- ########################################
Download this LIB
- ########################################
- # test for lib-file.fun
- ########################################
-
- use '..\lib\lib-file.fun';
-
- test();
-
- fun test()
- ..var f1 = 'c:\temp\admin\a.tmp';
- ..var f2 = 'c:\temp\admin\b.tmp';
- ..var f3 = 'c:\temp\admin\c.tmp';
- ..var fn = 'c:\temp\admin\?.tmp';
-
- ..f1.save('Hello, FUN.');
- ..?. f1.load();
-
- ..f1.copy(f2, f3);
- ..?. f2.load();
- ..?. f3.load();
-
- ..use '..\lib\lib-base.fun';
-
- ..foreach(fn.find(), (f){?.f});
-
- ..f1.move();
- ..f2.move();
- ..f3.move();
-
- ..foreach(fn.find(), (f){?.f});
- end fun;
Hello, FUN.
Hello, FUN.
Hello, FUN.
c:\temp\admin\a.tmp
c:\temp\admin\b.tmp
c:\temp\admin\c.tmp
Download this DEMO
8.5 Windows API
- ########################################
- # lib-winapi
- ########################################
- # built-in (2)
- ########################################
- # f.getapi(name, type)
- ########################################
- # f.@toCallback(nil, type, ptr)
- # f.@toCallback(obj, type, ptr)
- ########################################
- # 1. f.getapi(name, type)
- # f: file name (.dll)
- # name: proc name or address
- # type: xxx...:x
- # n(umber) i(nt) l(ong)
- # s(tring) w(idestr) a(nsistr) r(awstr)
- # p(ointer) v(oid) c(allback)
- # 2. how to call?
- # method.call() or method()
- #
- # e.g.:
- #
- # # get API
- # var msgbox = 'user32'.getapi('MessageBox', 'issi:i');
- #
- # # call via method.call()
- # msgbox.call(0, 'Hello, world!', 'Welcome...', 1);
- #
- # # or call via method()
- # msgbox(0, 'Hello, world!', 'Welcome...', 1);
- # 3. f.@toCallback(obj, type, ptr)
- # f # fun (function)
- # obj # object
- # type: xxx...:x # W - BSTR (OleString)
- # ptr: true or false # default false
- ########################################
Download this LIB
- ########################################
- # test for lib-winapi.fun
- ########################################
-
- use '..\lib\lib-winapi.fun';
-
- test();
-
- fun test()
- ..# constants
- ..var WM_SETTEXT = 12;
- ..var user32 = 'user32';
-
- ..# get APIs
- ..var findwin = user32.getapi('FindWindow', 'ss:i');
- ..var sendmsg = user32.getapi('SendMessage', 'iiip:i');
- ..var gettext = user32.getapi('GetWindowText', 'ipi:i');
-
- ..# settxt call sendmsg() - WM_SETTEXT
- ..var settxt(h, t) = sendmsg(h, WM_SETTEXT, 0, t);
-
- ..# gettxt call gettext()
- ..var gettxt = (h){
- ....result = ' '.x(256);
- ....var len = gettext(h, var result, 255);
- ....result = result.substr(len: len);
- ..};
-
- ..# call findwin via findwin.call()
- ..var handle = findwin.call('Shell_TrayWnd', '');
-
- ..# call settxt
- ..settxt(handle, 'New text.');
-
- ..# call gettxt
- ..?. '[' & gettxt(handle) & ']';
-
- ..# call settxt
- ..settxt(handle, '');
-
- ..# call gettxt
- ..?. '[' & gettxt(handle) & ']';
- end fun;
[New text.]
[]
Download this DEMO
8.6 Windows COM
- ########################################
- # lib-winole
- ########################################
- # built-in (2)
- ########################################
- # c.newobj(get = false)
- ########################################
- # f.@toEvent()
- # f.@toEvent(obj)
- # f.@toEvent(obj, Source, EventID, DispIDs)
- ########################################
- # 1. c.newobj(get = false)
- # c: class name or id
- # get: get an existing COM object
- # 2. Call method of ActiveX Object
- # obj.Method()
- # 3. Property get - @Property
- # x = obj.Property # or
- # x = obj.@Property() # or
- # x = obj.@Property(...) # for Property with params
- # 4. Property set - @@Property
- # obj.Property = x # or
- # obj.@@Property(x) # or
- # obj.@@Property(..., x) # for Property with params
- # 5. f.@toEvent()
- # f # fun (function)
- # obj # object
- # Source # Windows COM Object
- # EventID # IID
- # DispIDs # DispID(s)
- ########################################
Download this LIB
- ########################################
- # test for lib-winole.fun
- ########################################
-
- use '..\lib\lib-winole.fun';
- use '..\lib\lib-base.fun';
-
- test();
-
- fun test()
- ..var f1 = 'c:\temp\admin\1.tmp';
- ..var f2 = 'c:\temp\admin\2.tmp';
- ..var fn = 'c:\temp\admin\?.tmp';
-
- ..# Create ActiveX Object
- ..var sh = 'Wscript.Shell'.newobj();
-
- ..f1.save('Windows COM');
-
- ..# Call Method of COM Object
- ..sh.Run('cmd.exe /Ccopy %s %s'.format(f1, f2), 0, true);
-
- ..?. f2.load();
-
- ..foreach(fn.find(), (f){?.f; f.move()});
-
- ..var regex = 'VBScript.RegExp'.newobj();
- ..regex.Pattern = '^\d+$'; # Property set, or:
- ............................# regex.@@Pattern('^\d+$');
- ..?. regex.Pattern; # Property get, or:
- ............................# ?. regex.@Pattern();
- ..?. regex.Test('123'); # Call method
- ..?. regex.Test('abc');
- end fun;
Windows COM
c:\temp\admin\1.tmp
c:\temp\admin\2.tmp
^\d+$
True
False
Download this DEMO
8.7 Type conversions
- ########################################
- # lib-type
- ########################################
- # built-in (5)
- ########################################
- # a.toStr(index) # index: default 0, for array; -2: Unicode
- # a.toNum(ptr) #1-int, 2-float, 3-double, -1:ptr
- # a.toTime()
- # a.toByte(pos) # default 0
- # a.toChar()
- # a.fromByte(pos, byte)
- ########################################
Download this LIB
- ########################################
- # test for lib-type.fun
- ########################################
-
- use '..\lib\lib-type.fun';
-
- test();
-
- fun test()
- ..?. 123.toStr() & 456.toStr(); # Auto conversion, same to 123 & 456
- ..?. '123'.toNum() * 2; # Auto conversion, same to '123' * 2
-
- ..?. '2010-05-12 15:53:59'.toTime() + 100;
- ..?. 'a'.toByte();
- ..?. 0x41.toChar();
-
- ..if [] then # Auto convert to false
- ....?. '[]';
- ..else
- ....?. 'not []'; # HIT
- ..end if;
-
- ..if [1, 2, 3] then # Auto convert to true
- ....?. '[1, 2, 3]'; # HIT
- ..else
- ....?. 'not [1, 2, 3]';
- ..end if;
-
- ..if 'true' or 'yes' or 'NOT_BLANK' then # true <- true, yes, or other NOT BLANK string
- ....?. 'true or yes or NOT_BLANK';
- ..elsif 'false' or 'no' or '' then # false <- false, no, or BLANK
- ....?. 'false or no or BLANK';
- ..end if;
- end fun;
123456
246
2010-8-20 15:53:59
97
A
not []
[1, 2, 3]
true or yes or NOT_BLANK
Download this DEMO
8.8 Dynamic scripting
- ########################################
- # lib-dyns
- ########################################
- # built-in (1)
- ########################################
- # a.compile(inline = false) # file or string
- ########################################
- # a: file or string
- # inline: compile inline to access the current scope
- ########################################
Download this LIB
- ########################################
- # test for lib-dyns.fun
- ########################################
-
- use '..\lib\lib-dyns.fun';
-
- test();
-
- fun test()
- ..// compile a file
- ..var a = 'lib-file-test.fun'.compile();
- ..a(); // execute lib-file-test.fun
- ..a.test(); // call test() in lib-file-test.fun
-
- ..// compile a string
- ..var b = '?. 1 + 2;'.compile();
- ..b();
-
- ..// compile a string inline
- ..var c = 'b();'.compile(inline: true);
- ..c(); // call b() in the current scope
- end fun;
Hello, FUN.
Hello, FUN.
Hello, FUN.
c:\temp\admin\a.tmp
c:\temp\admin\b.tmp
c:\temp\admin\c.tmp
Hello, FUN.
Hello, FUN.
Hello, FUN.
c:\temp\admin\a.tmp
c:\temp\admin\b.tmp
c:\temp\admin\c.tmp
3
3
Download this DEMO
- var s = `var s = `$s`; ?. s.eval();`; ?. s.eval();
var s = `var s = `$s`; ?. s.eval();`; ?. s.eval();
Download this DEMO
9. Appendix
-
9.1 Demos
- #*======================================
- ....FFFFFFFF UU UU NN NN
- ....FF UU UU NNN NN
- ....FFFFFFF UU UU NN N NN
- ....FF UU UU NN N NN
- ....FF UU UU NN NNN
- ....FF UUUUUU NN NN
- ##======================================
-
- var out = output();
- class output()
- ..var head = (s){?. s & '.'.x(20-s.length())};
- ..var body = (s){?, ' '; ?. s};
- ..fun close()
- ....head('end');
- ....? '\r\n'.escape();
- ..end fun;
- end class;
-
- var idx;
- fun closure(o)
- ..var i = 0;
- ..return {
- ....o.body('index $i'.eval());
- ....result = i;
- ....i += 1;
- ..};
- end fun;
-
- fun test(n, f)
- ..out.head(n);
- ..idx = closure(out);
- ..f();
- ..out.close();
- end fun;
-
- test('if', {
- ..if idx() > idx() and idx() <= idx() then # short-circuit
- ....out.body('wrong');
- ..elsif idx() < idx() or idx() >= idx() then # short-circuit
- ....out.body('right');
- ..elsif idx() = idx() xor idx() <> idx() then
- ....out.body('error');
- ..else
- ....out.body('error');
- ..end if;
- });
-
- test('case', {
- ..case idx() is # @ <- idx()
- ....when idx() do
- ......out.body('wrong x()');
- ....when [idx(), idx()] do // or when new ...
- ......out.body('wrong [x]');
- ....when '^(%s)$'.format(idx()).toRegex() do
- ......out.body('wrong /x/');
- ....when '^($@)$'.eval().toRegex() do
- ......out.body('right /x/');
- ....when idx() do
- ......out.body('error');
- ....else
- ......out.body('error');
- ..end case;
- });
-
- test('loop', {
- ..loop
- ....exit when idx() < idx();
- ..end loop;
-
- ..for i = idx() to idx() step idx() loop
- ....out.body((i = 2) & ' - ' & i);
- ..end loop;
-
- ..for k: i in [a: idx(), b: idx(), c: idx()] do // or in new ...
- ....out.body('$k: $i'.eval());
- ..end do;
-
- ..while idx() < idx() do
- ....next when idx() mod 2 = 0;
- ....exit when idx() < idx();
- ..end do;
-
- ..out.body(idx() = 16);
- });
-
- test('try', {
- ..try
- ....var error() = 1 / 0;
- ....idx(); error(); idx();
- ..except # @ <- exception
- ....out.body('$@ at $@@()'.eval()); # eval $@@ as the last error
- ..finally
- ....out.body('final'.x(-1)); # reverse: a.x(-1)
- ..end try;
- });
if..................
index 0
index 1
index 2
index 3
right
end.................
case................
index 0
index 1
index 2
index 3
index 4
right /x/
end.................
loop................
index 0
index 1
index 2
index 3
index 4
True - 2
index 5
index 6
index 7
a: 5
b: 6
c: 7
index 8
index 9
index 10
index 11
index 12
index 13
index 14
index 15
index 16
True
end.................
try.................
index 0
Floating point division by zero at error/89()
lanif
end.................
Download this DEMO
9.2 Download
Fun.zip
-- THE END of Manual - Funlang.org