diff --git a/moo/README.md b/moo/README.md index b6da548..8ce7130 100644 --- a/moo/README.md +++ b/moo/README.md @@ -24,32 +24,28 @@ * C'X' -> charcter?? * C"X" -> character?? -* 'XXXX' string -* "XXXX" string with escaping - -* B"XXXXX" -> byte array with escaping? -* B'XXXXXX' -> byte array +* 'XXXX' string literal +* "XXXX" string litearl with escaping +* B'XXXXXX' -> byte array literal +* B"XXXXX" -> byte array literal with escaping * #XXXX symbol * #'XXXX' quoted symbol * #"XXXX" quoted symbol with escaping -* #\eNNNN error literal -* #\pNNNN smptr literal - -* %eNNNN <--------- -* %pNNNN <--------- +* #\e123 Error literal +* #\pB8000000 SmallPointer(smptr) literal * #() Array * #[] ByteArray -* #{} Dictionary +* #{} Dictionary - not supported yet The following are not literals. -* %() Array -* %[] ByteArray -* %{} Dictionary +* ##() Array expression +* ##[] ByteArray expression +* ##{} Dictionary expression * S%[] String literal with each character specified ** S%{A B C '\n'} diff --git a/moo/kernel/Collect.moo b/moo/kernel/Collect.moo index bf21ba7..59d503e 100644 --- a/moo/kernel/Collect.moo +++ b/moo/kernel/Collect.moo @@ -1284,7 +1284,7 @@ class Dictionary(AssociativeCollection) { /* [NOTE] * VM require Dictionary to implement new: and __put_assoc - * for the dictionary expression notation - %{ } + * for the dictionary expression notation - ##{ } */ // TODO: implement Dictionary as a Hashed List/Table or Red-Black Tree @@ -1299,7 +1299,7 @@ class Dictionary(AssociativeCollection) * to a dictionary with the dictionary/association expression notation * like this: * - * %{ 1 -> 20, #moo -> 999 } + * ##{ 1 -> 20, #moo -> 999 } * * it must return self for the way VM works. */ diff --git a/moo/kernel/Fcgi.moo b/moo/kernel/Fcgi.moo index 5046a0c..5a3d280 100644 --- a/moo/kernel/Fcgi.moo +++ b/moo/kernel/Fcgi.moo @@ -221,7 +221,7 @@ class Fcgi.ParamRecord(Fcgi.Record) { //# TODO: implement this... /* - (aString subStrings: %(char)) do: [:each | + (aString subStrings: ##(char)) do: [:each | equal := each indexOf: $=. equal = 0 ifTrue: [tempFields at: each put: nil] @@ -737,7 +737,7 @@ class MyObject(Object) fcgi := FcgiServer new. [ | ss | - fcgi start: %( + fcgi start: ##( SocketAddress fromString: ('[::]:' & base_port asString), SocketAddress fromString: ('0.0.0.0:' & (base_port + 1) asString) ). @@ -783,7 +783,7 @@ fcgi connect: addr. fcgi := FcgiServer new. [ | ss | - fcgi start: %( + fcgi start: ##( SocketAddress fromString: '[::]:7777', SocketAddress fromString: '0.0.0.0:7776' ). @@ -811,7 +811,7 @@ fcgi connect: addr. [ fcgi := FcgiServer new. [ - fcgi start: %( + fcgi start: ##( SocketAddress fromString: '[::]:7777', SocketAddress fromString: '0.0.0.0:7776' ). diff --git a/moo/kernel/Http.moo b/moo/kernel/Http.moo index 1999f69..1139f8d 100644 --- a/moo/kernel/Http.moo +++ b/moo/kernel/Http.moo @@ -420,7 +420,7 @@ class MyObject(Object) httpd := HttpServer new. [ | ss | - httpd start: %( + httpd start: ##( SocketAddress fromString: ('[::]:' & base_port asString), SocketAddress fromString: ('0.0.0.0:' & (base_port + 1) asString) ). @@ -462,7 +462,7 @@ httpd connect: addr. httpd := HttpServer new. [ | ss | - httpd start: %( + httpd start: ##( SocketAddress fromString: '[::]:7777', SocketAddress fromString: '0.0.0.0:7776' ). @@ -490,7 +490,7 @@ httpd connect: addr. [ httpd := HttpServer new. [ - httpd start: %( + httpd start: ##( SocketAddress fromString: '[::]:7777', SocketAddress fromString: '0.0.0.0:7776' ). diff --git a/moo/kernel/Mill.moo b/moo/kernel/Mill.moo index a468ff6..f3ca2c2 100644 --- a/moo/kernel/Mill.moo +++ b/moo/kernel/Mill.moo @@ -217,19 +217,19 @@ class MyObject(Object) a :=999. - a := %( + a := ##( 1, 2, a, 4, 1 + 1, - %( 1, 2, %(a, a := a + 1, a, if (a > 10) { a + 20 } ), 3), + ##( 1, 2, ##(a, a := a + 1, a, if (a > 10) { a + 20 } ), 3), 2 + 2, #'a b c' ). /* Dictionary ??? - a := %{ + a := ##{ key -> value , key -> value , key -> value , @@ -275,7 +275,7 @@ class MyObject(Object) }.*/ - a := %{ + a := ##{ 'aaa' -> 10, 'bbb' -> 20, 'bbb' -> 30, @@ -365,6 +365,6 @@ a free. /* pooldic XXD { #abc := #(1 2 3). - #def := %( 1, 3, 4 ). // syntax error - literal expected where %( is + #def := ##( 1, 3, 4 ). // syntax error - literal expected where ##( is } */ diff --git a/moo/kernel/X11.moo b/moo/kernel/X11.moo index f3832de..280a321 100644 --- a/moo/kernel/X11.moo +++ b/moo/kernel/X11.moo @@ -611,7 +611,7 @@ extend X11 at: self.LLEventType.CONFIGURE_NOTIFY put: #__handle_configure_notify:on:; at: self.LLEventType.CLIENT_MESSAGE put: #__handle_client_message:on:. */ - self.llevent_blocks := %{ + self.llevent_blocks := ##{ self.LLEventType.KEY_PRESS -> #__handle_key_event:on:, self.LLEventType.KEY_RELEASE -> #__handle_key_event:on:, self.LLEventType.BUTTON_PRESS -> #__handle_button_event:on:, diff --git a/moo/kernel/test-001.moo b/moo/kernel/test-001.moo index 68865dd..b2c17c5 100644 --- a/moo/kernel/test-001.moo +++ b/moo/kernel/test-001.moo @@ -161,7 +161,7 @@ extend MyObject { | tc limit | - tc := %( + tc := ##( // 0 - 4 [MyObject.Donkey v == 901982], [selfns.MyObject.Donkey v == 901982], @@ -396,7 +396,7 @@ extend MyObject [ | k | k := String new. - (%( 1 + 2, -21391239218392 * +291382913821, 19p10 div: 3 ) asOrderedCollection) doWithIndex: [:each :index | k := k & (index asString) & '=>' & (each asString) & ' '. ]. + (##( 1 + 2, -21391239218392 * +291382913821, 19p10 div: 3 ) asOrderedCollection) doWithIndex: [:each :index | k := k & (index asString) & '=>' & (each asString) & ' '. ]. k = '0=>3 1=>-6233041613697111534195832 2=>3.3333333333333333333 '. ], diff --git a/moo/kernel/test-002.moo b/moo/kernel/test-002.moo index e3dea80..9b33aa9 100644 --- a/moo/kernel/test-002.moo +++ b/moo/kernel/test-002.moo @@ -126,7 +126,7 @@ class MyObject(Object) sem wait. sem wait. - ^%( v, p ) // v must be 2000, p must be 6000 + ^##( v, p ) // v must be 2000, p must be 6000 } /* @@ -154,10 +154,10 @@ class MyObject(Object) { | tc limit | - tc := %( + tc := ##( // 0 - 4 [ (self test_quicksort: #(7 12 3 20 5 8 2) copy) = #(2 3 5 7 8 12 20)], - [ (self test_quicksort: %(99, 12, 18, 7, 12, 3, 20, 5, 8, 2)) = #(2 3 5 7 8 12 12 18 20 99)], + [ (self test_quicksort: ##(99, 12, 18, 7, 12, 3, 20, 5, 8, 2)) = #(2 3 5 7 8 12 12 18 20 99)], [ (self test_on_do_with: 10 with: 2) == 5 ], [ (self test_on_do_with: -10 with: 0) == 0 ], [ self test_ensure_with: -20945. self.ensure_tester_v == -20945 ], diff --git a/moo/kernel/test-003.moo b/moo/kernel/test-003.moo index 82a7528..c9d805e 100644 --- a/moo/kernel/test-003.moo +++ b/moo/kernel/test-003.moo @@ -34,7 +34,7 @@ class MyObject(Object) rec := [ :y :z | 22p108 - (18p815 - (16p1500 div: z) div: y) ]. - // results := %( 4.0, 425 div: 100.0 ) asOrderedCollection. + // results := ##( 4.0, 425 div: 100.0 ) asOrderedCollection. results := OrderedCollection new. results add: 4.0; add: (425.00 div: 100.00). @@ -56,7 +56,7 @@ class MyObject(Object) { | tc limit | - tc := %( + tc := ##( // 0 - 4 [(Object isKindOf: Class) == true], [(Object isKindOf: Apex) == true], diff --git a/moo/kernel/test-004.moo b/moo/kernel/test-004.moo index f6bda45..8e39cfa 100644 --- a/moo/kernel/test-004.moo +++ b/moo/kernel/test-004.moo @@ -56,7 +56,7 @@ sg removeSemaphore: s1. { | tc limit | - tc := %( + tc := ##( // 0 - 4 [self test_terminate == 180], [self test_sg == nil] diff --git a/moo/kernel/test-bi.moo b/moo/kernel/test-bi.moo index 0a89a9a..7da996d 100644 --- a/moo/kernel/test-bi.moo +++ b/moo/kernel/test-bi.moo @@ -42,8 +42,8 @@ class MyObject(Object) ffi := FFI new: 'libc.so.6'. now := ffi call: #time signature: 'l)i' arguments: #(0). - ////ffi call: #srand signature: 'i)' arguments: %(now). - ffi call: #srandom signature: 'i)' arguments: %(now). + ////ffi call: #srand signature: 'i)' arguments: ##(now). + ffi call: #srandom signature: 'i)' arguments: ##(now). [ divr_ubound := 16rFFFFFFFFFFFFFFFFFFFFFFFF. diff --git a/moo/lib/comp.c b/moo/lib/comp.c index aa991b0..0cea920 100644 --- a/moo/lib/comp.c +++ b/moo/lib/comp.c @@ -307,11 +307,10 @@ static MOO_INLINE int is_binselchar (moo_ooci_t c) { /* * binary-selector-character := - * '&' | '*' | '+' | '-' | '/' | + * '&' | '*' | '+' | '-' | '/' | '%' | * '<' | '>' | '=' | '@' | '~' | '|' * * - a comma is special in moo and doesn't form a binary selector. - * - a percent sign is special in moo and doesn't form a binary selector. * - an exclamation mark is excluded intentioinally because i can't tell * the method symbol #! from the comment introducer #!. * - a question mark forms a normal identifier. @@ -325,6 +324,7 @@ static MOO_INLINE int is_binselchar (moo_ooci_t c) case '+': case '-': case '/': + case '%': case '<': case '>': case '=': @@ -1169,7 +1169,7 @@ static int skip_comment (moo_t* moo) } else if (c == '#') { - /* handle #! or ## */ + /* handle #! */ /* save the last character */ lc = moo->c->lxc; @@ -1626,7 +1626,7 @@ static int get_charlit (moo_t* moo) return 0; } -static int get_strlit (moo_t* moo) +static int get_strlit (moo_t* moo, int byte_only) { /* * string-literal := single-quote string-character* single-quote @@ -1676,7 +1676,7 @@ static int get_strlit (moo_t* moo) return 0; } -static int get_string (moo_t* moo, moo_ooch_t end_char, moo_ooch_t esc_char, int regex, moo_oow_t preescaped) +static int get_string (moo_t* moo, moo_ooch_t end_char, moo_ooch_t esc_char, int byte_only, int regex, moo_oow_t preescaped) { moo_ooci_t c; moo_oow_t escaped = preescaped; @@ -1918,7 +1918,7 @@ retry: break; case '\'': /* string literal */ - if (get_strlit(moo) <= -1) return -1; + if (get_strlit(moo, 0) <= -1) return -1; break; case ':': @@ -1981,39 +1981,6 @@ retry: SET_TOKEN_TYPE (moo, MOO_IOTOK_SEMICOLON); goto single_char_token; - case '%': - SET_TOKEN_TYPE (moo, MOO_IOTOK_PERCENT); - ADD_TOKEN_CHAR (moo, c); - GET_CHAR_TO (moo, c); - - if (c == '(') - { - /* %( - array expression */ - ADD_TOKEN_CHAR (moo, c); - SET_TOKEN_TYPE (moo, MOO_IOTOK_PERCPAREN); - } - else if (c == '[') - { - /* %[ - byte-array expression */ - ADD_TOKEN_CHAR (moo, c); - SET_TOKEN_TYPE (moo, MOO_IOTOK_PERCBRACK); - } - else if (c == '{') - { - /* %{ - dictionary expression */ - ADD_TOKEN_CHAR (moo, c); - SET_TOKEN_TYPE (moo, MOO_IOTOK_PERCBRACE); - } - else - { - /* NOTE the percent sign not followed by ( or } is - * meaningless at this moment. however, i return - * it as a token so that the compiler anyway - * will fail eventually */ - unget_char (moo, &moo->c->lxc); - } - break; - case '#': ADD_TOKEN_CHAR (moo, c); GET_CHAR_TO (moo, c); @@ -2023,6 +1990,38 @@ retry: moo_setsynerr (moo, MOO_SYNERR_HLTNT, LEXER_LOC(moo), MOO_NULL); return -1; + case '#': + SET_TOKEN_TYPE (moo, MOO_IOTOK_DHASH); + ADD_TOKEN_CHAR (moo, c); + GET_CHAR_TO (moo, c); + if (c == '(') + { + /* ##( - array expression */ + ADD_TOKEN_CHAR (moo, c); + SET_TOKEN_TYPE (moo, MOO_IOTOK_DHASHPAREN); + } + else if (c == '[') + { + /* ##[ - byte array expression */ + ADD_TOKEN_CHAR (moo, c); + SET_TOKEN_TYPE (moo, MOO_IOTOK_DHASHBRACK); + } + else if (c == '{') + { + /* ##{ - dictionary expression */ + ADD_TOKEN_CHAR (moo, c); + SET_TOKEN_TYPE (moo, MOO_IOTOK_DHASHBRACE); + } + else + { + /* NOTE the double hashes not followed by (, [, or { is + * meaningless at this moment. however, i return + * it as a token so that the compiler anyway + * will fail eventually */ + unget_char (moo, &moo->c->lxc); + } + break; + case '(': /* #( - array literal */ ADD_TOKEN_CHAR (moo, c); @@ -2040,10 +2039,10 @@ retry: ADD_TOKEN_CHAR (moo, c); SET_TOKEN_TYPE (moo, MOO_IOTOK_HASHBRACE); break; - + case '\'': /* #'XXXX' - quoted symbol literal */ - if (get_strlit(moo) <= -1) return -1; /* reuse the string literal tokenizer */ + if (get_strlit(moo, 0) <= -1) return -1; /* reuse the string literal tokenizer */ SET_TOKEN_TYPE (moo, MOO_IOTOK_SYMLIT); /* change the symbol type to symbol */ break; @@ -2051,7 +2050,7 @@ retry: /* #"XXXX" - quoted symbol literal with C-style escape sequences. * if MOO_PRAGMA_QC is set, this part should never be reached */ MOO_ASSERT (moo, !(moo->c->pragma_flags & MOO_PRAGMA_QC)); - if (get_string(moo, '"', '\\', 0, 0) <= -1) return -1; + if (get_string(moo, '"', '\\', 0, 0, 0) <= -1) return -1; SET_TOKEN_TYPE (moo, MOO_IOTOK_SYMLIT); /* change the symbol type to symbol */ break; @@ -2214,12 +2213,12 @@ retry: case '"': /* if MOO_PRAGMA_QC is set, this part should never be reached */ MOO_ASSERT (moo, !(moo->c->pragma_flags & MOO_PRAGMA_QC)); - if (get_string(moo, '"', '\\', 0, 0) <= -1) return -1; + if (get_string(moo, '"', '\\', 0, 0, 0) <= -1) return -1; break; + case 'C': /* a character with a C-style escape sequence */ - case 'S': /* a string with a C-style escape sequences */ - case 'M': /* a symbol with a C-style escape sequences */ + case 'B': /* byte array in a string like notation */ { moo_ooci_t saved_c = c; @@ -2227,7 +2226,7 @@ retry: if (c == '\'') { /*GET_CHAR (moo);*/ - if (get_string(moo, '\'', '\\', 0, 0) <= -1) return -1; + if (get_strlit(moo, (saved_c == 'B')) <= -1) return -1; if (saved_c == 'C') { @@ -2238,9 +2237,28 @@ retry: } SET_TOKEN_TYPE (moo, MOO_IOTOK_CHARLIT); } - else if (saved_c == 'M') + else if (saved_c == 'B') { - SET_TOKEN_TYPE (moo, MOO_IOTOK_SYMLIT); + SET_TOKEN_TYPE (moo, MOO_IOTOK_BYTEARRAYLIT); + } + } + else if (c == '\"') + { + /*GET_CHAR (moo);*/ + if (get_string(moo, '\"', '\\', (saved_c == 'B'), 0, 0) <= -1) return -1; + + if (saved_c == 'C') + { + if (moo->c->tok.name.len != 1) + { + moo_setsynerr (moo, MOO_SYNERR_CHARLITINVAL, TOKEN_LOC(moo), TOKEN_NAME(moo)); + return -1; + } + SET_TOKEN_TYPE (moo, MOO_IOTOK_CHARLIT); + } + else if (saved_c == 'B') + { + SET_TOKEN_TYPE (moo, MOO_IOTOK_BYTEARRAYLIT); } } else @@ -2991,6 +3009,18 @@ static int add_symbol_literal (moo_t* moo, const moo_oocs_t* str, moo_oow_t offs return add_literal(moo, tmp, index); } +static int add_byte_array_literal (moo_t* moo, const moo_oocs_t* str, moo_oow_t* index) +{ + /* see read_byte_array_literal for comparision */ + moo_oop_t tmp; + moo_oow_t i; + + tmp = moo_instantiate(moo, moo->_byte_array, MOO_NULL, str->len); + if (!tmp) return -1; + for (i = 0; i < str->len; i++) MOO_OBJ_SET_BYTE_VAL(tmp, i, str->ptr[i]); + return add_literal(moo, tmp, index); +} + static MOO_INLINE int set_class_fqn (moo_t* moo, moo_cunit_class_t* cc, const moo_oocs_t* name) { if (copy_string_to(moo, name, &cc->fqn, &cc->fqn_capa, 0, '\0') <= -1) return -1; @@ -5026,7 +5056,7 @@ static int read_array_literal (moo_t* moo, int rdonly, moo_oop_t* xlit) } else if (TOKEN_TYPE(moo) == MOO_IOTOK_RPAREN) break; - lit = token_to_literal (moo, rdonly); + lit = token_to_literal(moo, rdonly); if (!lit || add_to_array_literal_buffer(moo, lit) <= -1) goto oops; GET_TOKEN_GOTO (moo, oops); @@ -5094,7 +5124,7 @@ static int _compile_array_expression (moo_t* moo, int closer_token, int bcode_ma moo_oow_t maip; moo_ioloc_t aeloc; - MOO_ASSERT (moo, TOKEN_TYPE(moo) == MOO_IOTOK_PERCPAREN || TOKEN_TYPE(moo) == MOO_IOTOK_PERCBRACK); + MOO_ASSERT (moo, TOKEN_TYPE(moo) == MOO_IOTOK_DHASHPAREN || TOKEN_TYPE(moo) == MOO_IOTOK_DHASHBRACK); maip = cc->mth.code.len; if (emit_single_param_instruction(moo, bcode_make, 0) <= -1) return -1; @@ -5159,7 +5189,7 @@ static int compile_dictionary_expression (moo_t* moo) moo_cunit_class_t* cc = (moo_cunit_class_t*)moo->c->cunit; moo_oow_t mdip; - MOO_ASSERT (moo, TOKEN_TYPE(moo) == MOO_IOTOK_PERCBRACE); + MOO_ASSERT (moo, TOKEN_TYPE(moo) == MOO_IOTOK_DHASHBRACE); GET_TOKEN (moo); /* read a token after :{ */ @@ -5425,6 +5455,11 @@ static int compile_expression_primary (moo_t* moo, const moo_oocs_t* ident, cons GET_TOKEN (moo); break; + case MOO_IOTOK_BYTEARRAYLIT: + /* B"xxxxx". see MOO_IOTOK_HASHBRACK below for comparision */ + if (add_byte_array_literal(moo, TOKEN_NAME(moo), &index) <= -1 || + emit_single_param_instruction(moo, BCODE_PUSH_LITERAL_0, index) <= -1) return -1; + break; case MOO_IOTOK_HASHPAREN: /* #( */ /*GET_TOKEN (moo);*/ @@ -5443,15 +5478,15 @@ static int compile_expression_primary (moo_t* moo, const moo_oocs_t* ident, cons break; #endif - case MOO_IOTOK_PERCPAREN: /* %( */ + case MOO_IOTOK_DHASHPAREN: /* ##( */ if (compile_array_expression(moo) <= -1) return -1; break; - case MOO_IOTOK_PERCBRACK: /* %[ */ + case MOO_IOTOK_DHASHBRACK: /* ##[ */ if (compile_bytearray_expression(moo) <= -1) return -1; break; - case MOO_IOTOK_PERCBRACE: /* %{ */ + case MOO_IOTOK_DHASHBRACE: /* ##{ */ if (compile_dictionary_expression(moo) <= -1) return -1; break; @@ -8832,6 +8867,24 @@ static moo_oop_t token_to_literal (moo_t* moo, int rdonly) return lit; } + case MOO_IOTOK_BYTEARRAYLIT: + { + moo_oop_t lit; + moo_oow_t i; + + lit = moo_instantiate(moo, moo->_byte_array, MOO_NULL, TOKEN_NAME_LEN(moo)); + if (lit) + { + for (i = 0; i < MOO_OBJ_GET_SIZE(lit); i++) MOO_OBJ_SET_BYTE_VAL(lit, i, TOKEN_NAME_PTR(moo)[i]); + if (rdonly) + { + MOO_ASSERT (moo, MOO_OOP_IS_POINTER(lit)); + MOO_OBJ_SET_FLAGS_RDONLY (lit, 1); + } + } + return lit; + } + case MOO_IOTOK_IDENT: case MOO_IOTOK_IDENT_DOTTED: { diff --git a/moo/lib/moo-prv.h b/moo/lib/moo-prv.h index a4a5313..9a12846 100644 --- a/moo/lib/moo-prv.h +++ b/moo/lib/moo-prv.h @@ -286,6 +286,7 @@ enum moo_iotok_type_t MOO_IOTOK_SCALEDFPDECLIT, /* NNpNNNN.NN e.g. 5p10.3 ===> 10.30000 */ MOO_IOTOK_ERRLIT, /* #\eNN */ MOO_IOTOK_SMPTRLIT, /* #\pXX */ + MOO_IOTOK_BYTEARRAYLIT, /* B"ab\x99\x77" */ MOO_IOTOK_NIL, MOO_IOTOK_SELF, @@ -317,7 +318,7 @@ enum moo_iotok_type_t MOO_IOTOK_KEYWORD, MOO_IOTOK_ASSIGN, /* := */ MOO_IOTOK_COLON, /* : */ - MOO_IOTOK_PERCENT, /* % */ + MOO_IOTOK_DHASH, /* ## */ MOO_IOTOK_RETURN, /* ^ */ MOO_IOTOK_LOCAL_RETURN, /* ^^ */ MOO_IOTOK_LBRACE, @@ -329,9 +330,9 @@ enum moo_iotok_type_t MOO_IOTOK_HASHPAREN, /* #( - array literal */ MOO_IOTOK_HASHBRACK, /* #[ - byte array literal */ MOO_IOTOK_HASHBRACE, /* #{ - dictionary literal */ - MOO_IOTOK_PERCPAREN, /* %( - array expression */ - MOO_IOTOK_PERCBRACK, /* %[ - byte array expression */ - MOO_IOTOK_PERCBRACE, /* %{ - dictionary expression */ + MOO_IOTOK_DHASHPAREN, /* #( - array expression */ + MOO_IOTOK_DHASHBRACK, /* #[ - byte array expression */ + MOO_IOTOK_DHASHBRACE, /* #{ - dictionary expression */ MOO_IOTOK_PERIOD, MOO_IOTOK_COMMA, MOO_IOTOK_SEMICOLON