+Following the Snobol tradition,
+LPeg defines patterns as first-class objects.
+That is, patterns are regular Lua values
+(represented by userdata).
+The library offers several functions to create
+and compose patterns.
+With the use of metamethods,
+several of these functions are provided as infix or prefix
+operators.
+On the one hand,
+the result is usually much more verbose than the typical
+encoding of patterns using the so called
+regular expressions
+(which typically are not regular expressions in the formal sense).
+On the other hand,
+first-class patterns allow much better documentation
+(as it is easy to comment the code,
+to break complex definitions in smaller parts, etc.)
+and are extensible,
+as we can define new functions to create and compose patterns.
+
+
+
+For a quick glance of the library,
+the following table summarizes its basic operations
+for creating patterns:
+
Matches patt behind the current position,
+ consuming no input
+
+
+
As a very simple example,
+lpeg.R("09")^1 creates a pattern that
+matches a non-empty sequence of digits.
+As a not so simple example,
+-lpeg.P(1)
+(which can be written as lpeg.P(-1),
+or simply -1 for operations expecting a pattern)
+matches an empty string only if it cannot match a single character;
+so, it succeeds only at the end of the subject.
+
+
+
+LPeg also offers the re module,
+which implements patterns following a regular-expression style
+(e.g., [09]+).
+(This module is 260 lines of Lua code,
+and of course it uses LPeg to parse regular expressions and
+translate them to regular LPeg patterns.)
+
+The matching function.
+It attempts to match the given pattern against the subject string.
+If the match succeeds,
+returns the index in the subject of the first character after the match,
+or the captured values
+(if the pattern captured any value).
+
+
+
+An optional numeric argument init makes the match
+start at that position in the subject string.
+As usual in Lua libraries,
+a negative value counts from the end.
+
+
+
+Unlike typical pattern-matching functions,
+match works only in anchored mode;
+that is, it tries to match the pattern with a prefix of
+the given subject string (at position init),
+not with an arbitrary substring of the subject.
+So, if we want to find a pattern anywhere in a string,
+we must either write a loop in Lua or write a pattern that
+matches anywhere.
+This second approach is easy and quite efficient;
+see examples.
+
+
+
lpeg.type (value)
+
+If the given value is a pattern,
+returns the string "pattern".
+Otherwise returns nil.
+
+
+
lpeg.version ()
+
+Returns a string with the running version of LPeg.
+
+
+
lpeg.setmaxstack (max)
+
+Sets a limit for the size of the backtrack stack used by LPeg to
+track calls and choices.
+(The default limit is 400.)
+Most well-written patterns need little backtrack levels and
+therefore you seldom need to change this limit;
+before changing it you should try to rewrite your
+pattern to avoid the need for extra space.
+Nevertheless, a few useful patterns may overflow.
+Also, with recursive grammars,
+subjects with deep recursion may also need larger limits.
+
+The following operations build patterns.
+All operations that expect a pattern as an argument
+may receive also strings, tables, numbers, booleans, or functions,
+which are translated to patterns according to
+the rules of function lpeg.P.
+
+
+
+
+
lpeg.P (value)
+
+Converts the given value into a proper pattern,
+according to the following rules:
+
+
+
+
+If the argument is a pattern,
+it is returned unmodified.
+
+
+
+If the argument is a string,
+it is translated to a pattern that matches the string literally.
+
+
+
+If the argument is a non-negative number n,
+the result is a pattern that matches exactly n characters.
+
+
+
+If the argument is a negative number -n,
+the result is a pattern that
+succeeds only if the input string has less than n characters left:
+lpeg.P(-n)
+is equivalent to -lpeg.P(n)
+(see the unary minus operation).
+
+
+
+If the argument is a boolean,
+the result is a pattern that always succeeds or always fails
+(according to the boolean value),
+without consuming any input.
+
+
+
+If the argument is a table,
+it is interpreted as a grammar
+(see Grammars).
+
+
+
+If the argument is a function,
+returns a pattern equivalent to a
+match-time capture over the empty string.
+
+
+
+
+
+
lpeg.B(patt)
+
+Returns a pattern that
+matches only if the input string at the current position
+is preceded by patt.
+Pattern patt must match only strings
+with some fixed length,
+and it cannot contain captures.
+
+
+
+Like the and predicate,
+this pattern never consumes any input,
+independently of success or failure.
+
+
+
+
lpeg.R ({range})
+
+Returns a pattern that matches any single character
+belonging to one of the given ranges.
+Each range is a string xy of length 2,
+representing all characters with code
+between the codes of x and y
+(both inclusive).
+
+
+
+As an example, the pattern
+lpeg.R("09") matches any digit,
+and lpeg.R("az", "AZ") matches any ASCII letter.
+
+
+
+
lpeg.S (string)
+
+Returns a pattern that matches any single character that
+appears in the given string.
+(The S stands for Set.)
+
+
+
+As an example, the pattern
+lpeg.S("+-*/") matches any arithmetic operator.
+
+
+
+Note that, if s is a character
+(that is, a string of length 1),
+then lpeg.P(s) is equivalent to lpeg.S(s)
+which is equivalent to lpeg.R(s..s).
+Note also that both lpeg.S("") and lpeg.R()
+are patterns that always fail.
+
+
+
+
lpeg.V (v)
+
+This operation creates a non-terminal (a variable)
+for a grammar.
+The created non-terminal refers to the rule indexed by v
+in the enclosing grammar.
+(See Grammars for details.)
+
+
+
+
lpeg.locale ([table])
+
+Returns a table with patterns for matching some character classes
+according to the current locale.
+The table has fields named
+alnum,
+alpha,
+cntrl,
+digit,
+graph,
+lower,
+print,
+punct,
+space,
+upper, and
+xdigit,
+each one containing a correspondent pattern.
+Each pattern matches any single character that belongs to its class.
+
+
+
+If called with an argument table,
+then it creates those fields inside the given table and
+returns that table.
+
+
+
+
#patt
+
+Returns a pattern that
+matches only if the input string matches patt,
+but without consuming any input,
+independently of success or failure.
+(This pattern is called an and predicate
+and it is equivalent to
+&patt in the original PEG notation.)
+
+
+
+
+This pattern never produces any capture.
+
+
+
+
-patt
+
+Returns a pattern that
+matches only if the input string does not match patt.
+It does not consume any input,
+independently of success or failure.
+(This pattern is equivalent to
+!patt in the original PEG notation.)
+
+
+
+As an example, the pattern
+-lpeg.P(1) matches only the end of string.
+
+
+
+This pattern never produces any captures,
+because either patt fails
+or -patt fails.
+(A failing pattern never produces captures.)
+
+
+
+
patt1 + patt2
+
+Returns a pattern equivalent to an ordered choice
+of patt1 and patt2.
+(This is denoted by patt1 / patt2 in the original PEG notation,
+not to be confused with the / operation in LPeg.)
+It matches either patt1 or patt2,
+with no backtracking once one of them succeeds.
+The identity element for this operation is the pattern
+lpeg.P(false),
+which always fails.
+
+
+
+If both patt1 and patt2 are
+character sets,
+this operation is equivalent to set union.
+
+Returns a pattern equivalent to !patt2 patt1.
+This pattern asserts that the input does not match
+patt2 and then matches patt1.
+
+
+
+When successful,
+this pattern produces all captures from patt1.
+It never produces any capture from patt2
+(as either patt2 fails or
+patt1 - patt2 fails).
+
+
+
+If both patt1 and patt2 are
+character sets,
+this operation is equivalent to set difference.
+Note that -patt is equivalent to "" - patt
+(or 0 - patt).
+If patt is a character set,
+1 - patt is its complement.
+
+
+
+
patt1 * patt2
+
+Returns a pattern that matches patt1
+and then matches patt2,
+starting where patt1 finished.
+The identity element for this operation is the
+pattern lpeg.P(true),
+which always succeeds.
+
+
+
+(LPeg uses the * operator
+[instead of the more obvious ..]
+both because it has
+the right priority and because in formal languages it is
+common to use a dot for denoting concatenation.)
+
+
+
+
patt^n
+
+If n is nonnegative,
+this pattern is
+equivalent to pattn patt*:
+It matches n or more occurrences of patt.
+
+
+
+Otherwise, when n is negative,
+this pattern is equivalent to (patt?)-n:
+It matches at most |n|
+occurrences of patt.
+
+
+
+In particular, patt^0 is equivalent to patt*,
+patt^1 is equivalent to patt+,
+and patt^-1 is equivalent to patt?
+in the original PEG notation.
+
+
+
+In all cases,
+the resulting pattern is greedy with no backtracking
+(also called a possessive repetition).
+That is, it matches only the longest possible sequence
+of matches for patt.
+
+With the use of Lua variables,
+it is possible to define patterns incrementally,
+with each new pattern using previously defined ones.
+However, this technique does not allow the definition of
+recursive patterns.
+For recursive patterns,
+we need real grammars.
+
+
+
+LPeg represents grammars with tables,
+where each entry is a rule.
+
+
+
+The call lpeg.V(v)
+creates a pattern that represents the nonterminal
+(or variable) with index v in a grammar.
+Because the grammar still does not exist when
+this function is evaluated,
+the result is an open reference to the respective rule.
+
+
+
+A table is fixed when it is converted to a pattern
+(either by calling lpeg.P or by using it wherein a
+pattern is expected).
+Then every open reference created by lpeg.V(v)
+is corrected to refer to the rule indexed by v in the table.
+
+
+
+When a table is fixed,
+the result is a pattern that matches its initial rule.
+The entry with index 1 in the table defines its initial rule.
+If that entry is a string,
+it is assumed to be the name of the initial rule.
+Otherwise, LPeg assumes that the entry 1 itself is the initial rule.
+
+
+
+As an example,
+the following grammar matches strings of a's and b's that
+have the same number of a's and b's:
+
+
+equalcount = lpeg.P{
+ "S"; -- initial rule name
+ S = "a" * lpeg.V"B" + "b" * lpeg.V"A" + "",
+ A = "a" * lpeg.V"S" + "b" * lpeg.V"A" * lpeg.V"A",
+ B = "b" * lpeg.V"S" + "a" * lpeg.V"B" * lpeg.V"B",
+} * -1
+
+
+It is equivalent to the following grammar in standard PEG notation:
+
+
+ S <- 'a' B / 'b' A / ''
+ A <- 'a' S / 'b' A A
+ B <- 'b' S / 'a' B B
+
+A capture is a pattern that produces values
+(the so called semantic information)
+according to what it matches.
+LPeg offers several kinds of captures,
+which produces values based on matches and combine these values to
+produce new values.
+Each capture may produce zero or more values.
+
+
+
+The following table summarizes the basic captures:
+
the returns of function applied to the captures
+ of patt; the application is done at match time
+
+
+
+A capture pattern produces its values only when it succeeds.
+For instance,
+the pattern lpeg.C(lpeg.P"a"^-1)
+produces the empty string when there is no "a"
+(because the pattern "a"? succeeds),
+while the pattern lpeg.C("a")^-1
+does not produce any value when there is no "a"
+(because the pattern "a" fails).
+A pattern inside a loop or inside a recursive structure
+produces values for each match.
+
+
+
+Usually,
+LPeg does not specify when (and if) it evaluates its captures.
+(As an example,
+consider the pattern lpeg.P"a" / func / 0.
+Because the "division" by 0 instructs LPeg to throw away the
+results from the pattern,
+LPeg may or may not call func.)
+Therefore, captures should avoid side effects.
+Moreover,
+most captures cannot affect the way a pattern matches a subject.
+The only exception to this rule is the
+so-called match-time capture.
+When a match-time capture matches,
+it forces the immediate evaluation of all its nested captures
+and then calls its corresponding function,
+which defines whether the match succeeds and also
+what values are produced.
+
+
+
lpeg.C (patt)
+
+Creates a simple capture,
+which captures the substring of the subject that matches patt.
+The captured value is a string.
+If patt has other captures,
+their values are returned after this one.
+
+
+
+
lpeg.Carg (n)
+
+Creates an argument capture.
+This pattern matches the empty string and
+produces the value given as the nth extra
+argument given in the call to lpeg.match.
+
+
+
+
lpeg.Cb (name)
+
+Creates a back capture.
+This pattern matches the empty string and
+produces the values produced by the most recent
+group capture named name
+(where name can be any Lua value).
+
+
+
+Most recent means the last
+complete
+outermost
+group capture with the given name.
+A Complete capture means that the entire pattern
+corresponding to the capture has matched.
+An Outermost capture means that the capture is not inside
+another complete capture.
+
+
+
+In the same way that LPeg does not specify when it evaluates captures,
+it does not specify whether it reuses
+values previously produced by the group
+or re-evaluates them.
+
+
+
lpeg.Cc ([value, ...])
+
+Creates a constant capture.
+This pattern matches the empty string and
+produces all given values as its captured values.
+
+
+
+
lpeg.Cf (patt, func)
+
+Creates a fold capture.
+If patt produces a list of captures
+C1 C2 ... Cn,
+this capture will produce the value
+func(...func(func(C1, C2), C3)...,
+ Cn),
+that is, it will fold
+(or accumulate, or reduce)
+the captures from patt using function func.
+
+
+
+This capture assumes that patt should produce
+at least one capture with at least one value (of any type),
+which becomes the initial value of an accumulator.
+(If you need a specific initial value,
+you may prefix a constant capture to patt.)
+For each subsequent capture,
+LPeg calls func
+with this accumulator as the first argument and all values produced
+by the capture as extra arguments;
+the first result from this call
+becomes the new value for the accumulator.
+The final value of the accumulator becomes the captured value.
+
+
+
+As an example,
+the following pattern matches a list of numbers separated
+by commas and returns their addition:
+
+
+-- matches a numeral and captures its numerical value
+number = lpeg.R"09"^1 / tonumber
+
+-- matches a list of numbers, capturing their values
+list = number * ("," * number)^0
+
+-- auxiliary function to add two numbers
+function add (acc, newvalue) return acc + newvalue end
+
+-- folds the list of numbers adding them
+sum = lpeg.Cf(list, add)
+
+-- example of use
+print(sum:match("10,30,43")) --> 83
+
+
+
+
lpeg.Cg (patt [, name])
+
+Creates a group capture.
+It groups all values returned by patt
+into a single capture.
+The group may be anonymous (if no name is given)
+or named with the given name
+(which can be any non-nil Lua value).
+
+
+
+An anonymous group serves to join values from several captures into
+a single capture.
+A named group has a different behavior.
+In most situations, a named group returns no values at all.
+Its values are only relevant for a following
+back capture or when used
+inside a table capture.
+
+
+
+
lpeg.Cp ()
+
+Creates a position capture.
+It matches the empty string and
+captures the position in the subject where the match occurs.
+The captured value is a number.
+
+
+
+
lpeg.Cs (patt)
+
+Creates a substitution capture,
+which captures the substring of the subject that matches patt,
+with substitutions.
+For any capture inside patt with a value,
+the substring that matched the capture is replaced by the capture value
+(which should be a string).
+The final captured value is the string resulting from
+all replacements.
+
+
+
+
lpeg.Ct (patt)
+
+Creates a table capture.
+This capture returns a table with all values from all anonymous captures
+made by patt inside this table in successive integer keys,
+starting at 1.
+Moreover,
+for each named capture group created by patt,
+the first value of the group is put into the table
+with the group name as its key.
+The captured value is only the table.
+
+
+
+
patt / string
+
+Creates a string capture.
+It creates a capture string based on string.
+The captured value is a copy of string,
+except that the character % works as an escape character:
+any sequence in string of the form %n,
+with n between 1 and 9,
+stands for the match of the n-th capture in patt.
+The sequence %0 stands for the whole match.
+The sequence %% stands for a single %.
+
+
+
+
patt / number
+
+Creates a numbered capture.
+For a non-zero number,
+the captured value is the n-th value
+captured by patt.
+When number is zero,
+there are no captured values.
+
+
+
+
patt / table
+
+Creates a query capture.
+It indexes the given table using as key the first value captured by
+patt,
+or the whole match if patt produced no value.
+The value at that index is the final value of the capture.
+If the table does not have that key,
+there is no captured value.
+
+
+
+
patt / function
+
+Creates a function capture.
+It calls the given function passing all captures made by
+patt as arguments,
+or the whole match if patt made no capture.
+The values returned by the function
+are the final values of the capture.
+In particular,
+if function returns no value,
+there is no captured value.
+
+
+
+
lpeg.Cmt(patt, function)
+
+Creates a match-time capture.
+Unlike all other captures,
+this one is evaluated immediately when a match occurs
+(even if it is part of a larger pattern that fails later).
+It forces the immediate evaluation of all its nested captures
+and then calls function.
+
+
+
+The given function gets as arguments the entire subject,
+the current position (after the match of patt),
+plus any capture values produced by patt.
+
+
+
+The first value returned by function
+defines how the match happens.
+If the call returns a number,
+the match succeeds
+and the returned number becomes the new current position.
+(Assuming a subject s and current position i,
+the returned number must be in the range [i, len(s) + 1].)
+If the call returns true,
+the match succeeds without consuming any input.
+(So, to return true is equivalent to return i.)
+If the call returns false, nil, or no value,
+the match fails.
+
+
+
+Any extra values returned by the function become the
+values produced by the capture.
+
+This example shows a very simple but complete program
+that builds and uses a pattern:
+
+
+local lpeg = require "lpeg"
+
+-- matches a word followed by end-of-string
+p = lpeg.R"az"^1 * -1
+
+print(p:match("hello")) --> 6
+print(lpeg.match(p, "hello")) --> 6
+print(p:match("1 hello")) --> nil
+
+
+The pattern is simply a sequence of one or more lower-case letters
+followed by the end of string (-1).
+The program calls match both as a method
+and as a function.
+In both sucessful cases,
+the match returns
+the index of the first character after the match,
+which is the string length plus one.
+
+
+
+
Name-value lists
+
+This example parses a list of name-value pairs and returns a table
+with those pairs:
+
+
+lpeg.locale(lpeg) -- adds locale entries into 'lpeg' table
+
+local space = lpeg.space^0
+local name = lpeg.C(lpeg.alpha^1) * space
+local sep = lpeg.S(",;") * space
+local pair = lpeg.Cg(name * "=" * space * name) * sep^-1
+local list = lpeg.Cf(lpeg.Ct("") * pair^0, rawset)
+t = list:match("a=b, c = hi; next = pi") --> { a = "b", c = "hi", next = "pi" }
+
+
+Each pair has the format name = name followed by
+an optional separator (a comma or a semicolon).
+The pair pattern encloses the pair in a group pattern,
+so that the names become the values of a single capture.
+The list pattern then folds these captures.
+It starts with an empty table,
+created by a table capture matching an empty string;
+then for each capture (a pair of names) it applies rawset
+over the accumulator (the table) and the capture values (the pair of names).
+rawset returns the table itself,
+so the accumulator is always the table.
+
+
+
Splitting a string
+
+The following code builds a pattern that
+splits a string using a given pattern
+sep as a separator:
+
+
+function split (s, sep)
+ sep = lpeg.P(sep)
+ local elem = lpeg.C((1 - sep)^0)
+ local p = elem * (sep * elem)^0
+ return lpeg.match(p, s)
+end
+
+
+First the function ensures that sep is a proper pattern.
+The pattern elem is a repetition of zero of more
+arbitrary characters as long as there is not a match against
+the separator.
+It also captures its match.
+The pattern p matches a list of elements separated
+by sep.
+
+
+
+If the split results in too many values,
+it may overflow the maximum number of values
+that can be returned by a Lua function.
+In this case,
+we can collect these values in a table:
+
+
+function split (s, sep)
+ sep = lpeg.P(sep)
+ local elem = lpeg.C((1 - sep)^0)
+ local p = lpeg.Ct(elem * (sep * elem)^0) -- make a table capture
+ return lpeg.match(p, s)
+end
+
+
+
+
Searching for a pattern
+
+The primitive match works only in anchored mode.
+If we want to find a pattern anywhere in a string,
+we must write a pattern that matches anywhere.
+
+
+
+Because patterns are composable,
+we can write a function that,
+given any arbitrary pattern p,
+returns a new pattern that searches for p
+anywhere in a string.
+There are several ways to do the search.
+One way is like this:
+
+This grammar has a straight reading:
+it matches p or skips one character and tries again.
+
+
+
+If we want to know where the pattern is in the string
+(instead of knowing only that it is there somewhere),
+we can add position captures to the pattern:
+
+
+local I = lpeg.Cp()
+function anywhere (p)
+ return lpeg.P{ I * p * I + 1 * lpeg.V(1) }
+end
+
+print(anywhere("world"):match("hello world!")) -> 7 12
+
+
+
+Another option for the search is like this:
+
+
+local I = lpeg.Cp()
+function anywhere (p)
+ return (1 - lpeg.P(p))^0 * I * p * I
+end
+
+
+Again the pattern has a straight reading:
+it skips as many characters as possible while not matching p,
+and then matches p (plus appropriate captures).
+
+
+
+If we want to look for a pattern only at word boundaries,
+we can use the following transformer:
+
+Reading the first (and only) rule of the given grammar,
+we have that a balanced string is
+an open parenthesis,
+followed by zero or more repetitions of either
+a non-parenthesis character or
+a balanced string (lpeg.V(1)),
+followed by a closing parenthesis.
+
+
+
+
Global substitution
+
+The next example does a job somewhat similar to string.gsub.
+It receives a pattern and a replacement value,
+and substitutes the replacement value for all occurrences of the pattern
+in a given string:
+
+A field is either a quoted field
+(which may contain any character except an individual quote,
+which may be written as two quotes that are replaced by one)
+or an unquoted field
+(which cannot contain commas, newlines, or quotes).
+A record is a list of fields separated by commas,
+ending with a newline or the string end (-1).
+
+
+
+As it is,
+the previous pattern returns each field as a separated result.
+If we add a table capture in the definition of record,
+the pattern will return instead a single table
+containing all fields:
+
+It is not difficult to use LPeg to convert a string from
+UTF-8 encoding to Latin 1 (ISO 8859-1):
+
+
+
+-- convert a two-byte UTF-8 sequence to a Latin 1 character
+local function f2 (s)
+ local c1, c2 = string.byte(s, 1, 2)
+ return string.char(c1 * 64 + c2 - 12416)
+end
+
+local utf8 = lpeg.R("\0\127")
+ + lpeg.R("\194\195") * lpeg.R("\128\191") / f2
+
+local decode_pattern = lpeg.Cs(utf8^0) * -1
+
+
+In this code,
+the definition of UTF-8 is already restricted to the
+Latin 1 range (from 0 to 255).
+Any encoding outside this range (as well as any invalid encoding)
+will not match that pattern.
+
+
+
+As the definition of decode_pattern demands that
+the pattern matches the whole input (because of the -1 at its end),
+any invalid string will simply fail to match,
+without any useful information about the problem.
+We can improve this situation redefining decode_pattern
+as follows:
+
+
+local function er (_, i) error("invalid encoding at position " .. i) end
+
+local decode_pattern = lpeg.Cs(utf8^0) * (-1 + lpeg.P(er))
+
+
+Now, if the pattern utf8^0 stops
+before the end of the string,
+an appropriate error function is called.
+
+
+
+
UTF-8 and Unicode
+
+We can extend the previous patterns to handle all Unicode code points.
+Of course,
+we cannot translate them to Latin 1 or any other one-byte encoding.
+Instead, our translation results in a array with the code points
+represented as numbers.
+The full code is here:
+
+A long string in Lua starts with the pattern [=*[
+and ends at the first occurrence of ]=*] with
+exactly the same number of equal signs.
+If the opening brackets are followed by a newline,
+this newline is discarded
+(that is, it is not part of the string).
+
+
+
+To match a long string in Lua,
+the pattern must capture the first repetition of equal signs and then,
+whenever it finds a candidate for closing the string,
+check whether it has the same number of equal signs.
+
+
+
+equals = lpeg.P"="^0
+open = "[" * lpeg.Cg(equals, "init") * "[" * lpeg.P"\n"^-1
+close = "]" * lpeg.C(equals) * "]"
+closeeq = lpeg.Cmt(close * lpeg.Cb("init"), function (s, i, a, b) return a == b end)
+string = open * lpeg.C((lpeg.P(1) - closeeq)^0) * close / 1
+
+
+
+The open pattern matches [=*[,
+capturing the repetitions of equal signs in a group named init;
+it also discharges an optional newline, if present.
+The close pattern matches ]=*],
+also capturing the repetitions of equal signs.
+The closeeq pattern first matches close;
+then it uses a back capture to recover the capture made
+by the previous open,
+which is named init;
+finally it uses a match-time capture to check
+whether both captures are equal.
+The string pattern starts with an open,
+then it goes as far as possible until matching closeeq,
+and then matches the final close.
+The final numbered capture simply discards
+the capture made by close.
+
+
+
+
Arithmetic expressions
+
+This example is a complete parser and evaluator for simple
+arithmetic expressions.
+We write it in two styles.
+The first approach first builds a syntax tree and then
+traverses this tree to compute the expression value:
+
+
+-- Lexical Elements
+local Space = lpeg.S(" \n\t")^0
+local Number = lpeg.C(lpeg.P"-"^-1 * lpeg.R("09")^1) * Space
+local TermOp = lpeg.C(lpeg.S("+-")) * Space
+local FactorOp = lpeg.C(lpeg.S("*/")) * Space
+local Open = "(" * Space
+local Close = ")" * Space
+
+-- Grammar
+local Exp, Term, Factor = lpeg.V"Exp", lpeg.V"Term", lpeg.V"Factor"
+G = lpeg.P{ Exp,
+ Exp = lpeg.Ct(Term * (TermOp * Term)^0);
+ Term = lpeg.Ct(Factor * (FactorOp * Factor)^0);
+ Factor = Number + Open * Exp * Close;
+}
+
+G = Space * G * -1
+
+-- Evaluator
+function eval (x)
+ if type(x) == "string" then
+ return tonumber(x)
+ else
+ local op1 = eval(x[1])
+ for i = 2, #x, 2 do
+ local op = x[i]
+ local op2 = eval(x[i + 1])
+ if (op == "+") then op1 = op1 + op2
+ elseif (op == "-") then op1 = op1 - op2
+ elseif (op == "*") then op1 = op1 * op2
+ elseif (op == "/") then op1 = op1 / op2
+ end
+ end
+ return op1
+ end
+end
+
+-- Parser/Evaluator
+function evalExp (s)
+ local t = lpeg.match(G, s)
+ if not t then error("syntax error", 2) end
+ return eval(t)
+end
+
+-- small example
+print(evalExp"3 + 5*9 / (1+1) - 12") --> 13.5
+
+
+
+The second style computes the expression value on the fly,
+without building the syntax tree.
+The following grammar takes this approach.
+(It assumes the same lexical elements as before.)
+
+
+-- Auxiliary function
+function eval (v1, op, v2)
+ if (op == "+") then return v1 + v2
+ elseif (op == "-") then return v1 - v2
+ elseif (op == "*") then return v1 * v2
+ elseif (op == "/") then return v1 / v2
+ end
+end
+
+-- Grammar
+local V = lpeg.V
+G = lpeg.P{ "Exp",
+ Exp = lpeg.Cf(V"Term" * lpeg.Cg(TermOp * V"Term")^0, eval);
+ Term = lpeg.Cf(V"Factor" * lpeg.Cg(FactorOp * V"Factor")^0, eval);
+ Factor = Number / tonumber + Open * V"Exp" * Close;
+}
+
+-- small example
+print(lpeg.match(G, "3 + 5*9 / (1+1) - 12")) --> 13.5
+
+
+Note the use of the fold (accumulator) capture.
+To compute the value of an expression,
+the accumulator starts with the value of the first term,
+and then applies eval over
+the accumulator, the operator,
+and the new term for each repetition.
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"),
+to deal in the Software without restriction,
+including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software,
+and to permit persons to whom the Software is
+furnished to do so,
+subject to the following conditions:
+
+
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+
+
+
+
+
+
+
+
diff --git a/luaclib/src/lpeg/lpprint.c b/luaclib/src/lpeg/lpprint.c
new file mode 100644
index 00000000..df62cbee
--- /dev/null
+++ b/luaclib/src/lpeg/lpprint.c
@@ -0,0 +1,244 @@
+/*
+** $Id: lpprint.c $
+** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
+*/
+
+#include
+#include
+#include
+
+
+#include "lptypes.h"
+#include "lpprint.h"
+#include "lpcode.h"
+
+
+#if defined(LPEG_DEBUG)
+
+/*
+** {======================================================
+** Printing patterns (for debugging)
+** =======================================================
+*/
+
+
+void printcharset (const byte *st) {
+ int i;
+ printf("[");
+ for (i = 0; i <= UCHAR_MAX; i++) {
+ int first = i;
+ while (testchar(st, i) && i <= UCHAR_MAX) i++;
+ if (i - 1 == first) /* unary range? */
+ printf("(%02x)", first);
+ else if (i - 1 > first) /* non-empty range? */
+ printf("(%02x-%02x)", first, i - 1);
+ }
+ printf("]");
+}
+
+
+static const char *capkind (int kind) {
+ const char *const modes[] = {
+ "close", "position", "constant", "backref",
+ "argument", "simple", "table", "function",
+ "query", "string", "num", "substitution", "fold",
+ "runtime", "group"};
+ return modes[kind];
+}
+
+
+static void printjmp (const Instruction *op, const Instruction *p) {
+ printf("-> %d", (int)(p + (p + 1)->offset - op));
+}
+
+
+void printinst (const Instruction *op, const Instruction *p) {
+ const char *const names[] = {
+ "any", "char", "set",
+ "testany", "testchar", "testset",
+ "span", "behind",
+ "ret", "end",
+ "choice", "jmp", "call", "open_call",
+ "commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",
+ "fullcapture", "opencapture", "closecapture", "closeruntime"
+ };
+ printf("%02ld: %s ", (long)(p - op), names[p->i.code]);
+ switch ((Opcode)p->i.code) {
+ case IChar: {
+ printf("'%c'", p->i.aux);
+ break;
+ }
+ case ITestChar: {
+ printf("'%c'", p->i.aux); printjmp(op, p);
+ break;
+ }
+ case IFullCapture: {
+ printf("%s (size = %d) (idx = %d)",
+ capkind(getkind(p)), getoff(p), p->i.key);
+ break;
+ }
+ case IOpenCapture: {
+ printf("%s (idx = %d)", capkind(getkind(p)), p->i.key);
+ break;
+ }
+ case ISet: {
+ printcharset((p+1)->buff);
+ break;
+ }
+ case ITestSet: {
+ printcharset((p+2)->buff); printjmp(op, p);
+ break;
+ }
+ case ISpan: {
+ printcharset((p+1)->buff);
+ break;
+ }
+ case IOpenCall: {
+ printf("-> %d", (p + 1)->offset);
+ break;
+ }
+ case IBehind: {
+ printf("%d", p->i.aux);
+ break;
+ }
+ case IJmp: case ICall: case ICommit: case IChoice:
+ case IPartialCommit: case IBackCommit: case ITestAny: {
+ printjmp(op, p);
+ break;
+ }
+ default: break;
+ }
+ printf("\n");
+}
+
+
+void printpatt (Instruction *p, int n) {
+ Instruction *op = p;
+ while (p < op + n) {
+ printinst(op, p);
+ p += sizei(p);
+ }
+}
+
+
+#if defined(LPEG_DEBUG)
+static void printcap (Capture *cap) {
+ printf("%s (idx: %d - size: %d) -> %p\n",
+ capkind(cap->kind), cap->idx, cap->siz, cap->s);
+}
+
+
+void printcaplist (Capture *cap, Capture *limit) {
+ printf(">======\n");
+ for (; cap->s && (limit == NULL || cap < limit); cap++)
+ printcap(cap);
+ printf("=======\n");
+}
+#endif
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Printing trees (for debugging)
+** =======================================================
+*/
+
+static const char *tagnames[] = {
+ "char", "set", "any",
+ "true", "false",
+ "rep",
+ "seq", "choice",
+ "not", "and",
+ "call", "opencall", "rule", "grammar",
+ "behind",
+ "capture", "run-time"
+};
+
+
+void printtree (TTree *tree, int ident) {
+ int i;
+ for (i = 0; i < ident; i++) printf(" ");
+ printf("%s", tagnames[tree->tag]);
+ switch (tree->tag) {
+ case TChar: {
+ int c = tree->u.n;
+ if (isprint(c))
+ printf(" '%c'\n", c);
+ else
+ printf(" (%02X)\n", c);
+ break;
+ }
+ case TSet: {
+ printcharset(treebuffer(tree));
+ printf("\n");
+ break;
+ }
+ case TOpenCall: case TCall: {
+ assert(sib2(tree)->tag == TRule);
+ printf(" key: %d (rule: %d)\n", tree->key, sib2(tree)->cap);
+ break;
+ }
+ case TBehind: {
+ printf(" %d\n", tree->u.n);
+ printtree(sib1(tree), ident + 2);
+ break;
+ }
+ case TCapture: {
+ printf(" kind: '%s' key: %d\n", capkind(tree->cap), tree->key);
+ printtree(sib1(tree), ident + 2);
+ break;
+ }
+ case TRule: {
+ printf(" n: %d key: %d\n", tree->cap, tree->key);
+ printtree(sib1(tree), ident + 2);
+ break; /* do not print next rule as a sibling */
+ }
+ case TGrammar: {
+ TTree *rule = sib1(tree);
+ printf(" %d\n", tree->u.n); /* number of rules */
+ for (i = 0; i < tree->u.n; i++) {
+ printtree(rule, ident + 2);
+ rule = sib2(rule);
+ }
+ assert(rule->tag == TTrue); /* sentinel */
+ break;
+ }
+ default: {
+ int sibs = numsiblings[tree->tag];
+ printf("\n");
+ if (sibs >= 1) {
+ printtree(sib1(tree), ident + 2);
+ if (sibs >= 2)
+ printtree(sib2(tree), ident + 2);
+ }
+ break;
+ }
+ }
+}
+
+
+void printktable (lua_State *L, int idx) {
+ int n, i;
+ lua_getuservalue(L, idx);
+ if (lua_isnil(L, -1)) /* no ktable? */
+ return;
+ n = lua_rawlen(L, -1);
+ printf("[");
+ for (i = 1; i <= n; i++) {
+ printf("%d = ", i);
+ lua_rawgeti(L, -1, i);
+ if (lua_isstring(L, -1))
+ printf("%s ", lua_tostring(L, -1));
+ else
+ printf("%s ", lua_typename(L, lua_type(L, -1)));
+ lua_pop(L, 1);
+ }
+ printf("]\n");
+ /* leave ktable at the stack */
+}
+
+/* }====================================================== */
+
+#endif
diff --git a/luaclib/src/lpeg/lpprint.h b/luaclib/src/lpeg/lpprint.h
new file mode 100644
index 00000000..15ef121d
--- /dev/null
+++ b/luaclib/src/lpeg/lpprint.h
@@ -0,0 +1,36 @@
+/*
+** $Id: lpprint.h $
+*/
+
+
+#if !defined(lpprint_h)
+#define lpprint_h
+
+
+#include "lptree.h"
+#include "lpvm.h"
+
+
+#if defined(LPEG_DEBUG)
+
+void printpatt (Instruction *p, int n);
+void printtree (TTree *tree, int ident);
+void printktable (lua_State *L, int idx);
+void printcharset (const byte *st);
+void printcaplist (Capture *cap, Capture *limit);
+void printinst (const Instruction *op, const Instruction *p);
+
+#else
+
+#define printktable(L,idx) \
+ luaL_error(L, "function only implemented in debug mode")
+#define printtree(tree,i) \
+ luaL_error(L, "function only implemented in debug mode")
+#define printpatt(p,n) \
+ luaL_error(L, "function only implemented in debug mode")
+
+#endif
+
+
+#endif
+
diff --git a/luaclib/src/lpeg/lptree.c b/luaclib/src/lpeg/lptree.c
new file mode 100644
index 00000000..5c8de947
--- /dev/null
+++ b/luaclib/src/lpeg/lptree.c
@@ -0,0 +1,1305 @@
+/*
+** $Id: lptree.c $
+** Copyright 2013, Lua.org & PUC-Rio (see 'lpeg.html' for license)
+*/
+
+#include
+#include
+#include
+
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "lptypes.h"
+#include "lpcap.h"
+#include "lpcode.h"
+#include "lpprint.h"
+#include "lptree.h"
+
+
+/* number of siblings for each tree */
+const byte numsiblings[] = {
+ 0, 0, 0, /* char, set, any */
+ 0, 0, /* true, false */
+ 1, /* rep */
+ 2, 2, /* seq, choice */
+ 1, 1, /* not, and */
+ 0, 0, 2, 1, /* call, opencall, rule, grammar */
+ 1, /* behind */
+ 1, 1 /* capture, runtime capture */
+};
+
+
+static TTree *newgrammar (lua_State *L, int arg);
+
+
+/*
+** returns a reasonable name for value at index 'idx' on the stack
+*/
+static const char *val2str (lua_State *L, int idx) {
+ const char *k = lua_tostring(L, idx);
+ if (k != NULL)
+ return lua_pushfstring(L, "%s", k);
+ else
+ return lua_pushfstring(L, "(a %s)", luaL_typename(L, idx));
+}
+
+
+/*
+** Fix a TOpenCall into a TCall node, using table 'postable' to
+** translate a key to its rule address in the tree. Raises an
+** error if key does not exist.
+*/
+static void fixonecall (lua_State *L, int postable, TTree *g, TTree *t) {
+ int n;
+ lua_rawgeti(L, -1, t->key); /* get rule's name */
+ lua_gettable(L, postable); /* query name in position table */
+ n = lua_tonumber(L, -1); /* get (absolute) position */
+ lua_pop(L, 1); /* remove position */
+ if (n == 0) { /* no position? */
+ lua_rawgeti(L, -1, t->key); /* get rule's name again */
+ luaL_error(L, "rule '%s' undefined in given grammar", val2str(L, -1));
+ }
+ t->tag = TCall;
+ t->u.ps = n - (t - g); /* position relative to node */
+ assert(sib2(t)->tag == TRule);
+ sib2(t)->key = t->key; /* fix rule's key */
+}
+
+
+/*
+** Transform left associative constructions into right
+** associative ones, for sequence and choice; that is:
+** (t11 + t12) + t2 => t11 + (t12 + t2)
+** (t11 * t12) * t2 => t11 * (t12 * t2)
+** (that is, Op (Op t11 t12) t2 => Op t11 (Op t12 t2))
+*/
+static void correctassociativity (TTree *tree) {
+ TTree *t1 = sib1(tree);
+ assert(tree->tag == TChoice || tree->tag == TSeq);
+ while (t1->tag == tree->tag) {
+ int n1size = tree->u.ps - 1; /* t1 == Op t11 t12 */
+ int n11size = t1->u.ps - 1;
+ int n12size = n1size - n11size - 1;
+ memmove(sib1(tree), sib1(t1), n11size * sizeof(TTree)); /* move t11 */
+ tree->u.ps = n11size + 1;
+ sib2(tree)->tag = tree->tag;
+ sib2(tree)->u.ps = n12size + 1;
+ }
+}
+
+
+/*
+** Make final adjustments in a tree. Fix open calls in tree 't',
+** making them refer to their respective rules or raising appropriate
+** errors (if not inside a grammar). Correct associativity of associative
+** constructions (making them right associative). Assume that tree's
+** ktable is at the top of the stack (for error messages).
+*/
+static void finalfix (lua_State *L, int postable, TTree *g, TTree *t) {
+ tailcall:
+ switch (t->tag) {
+ case TGrammar: /* subgrammars were already fixed */
+ return;
+ case TOpenCall: {
+ if (g != NULL) /* inside a grammar? */
+ fixonecall(L, postable, g, t);
+ else { /* open call outside grammar */
+ lua_rawgeti(L, -1, t->key);
+ luaL_error(L, "rule '%s' used outside a grammar", val2str(L, -1));
+ }
+ break;
+ }
+ case TSeq: case TChoice:
+ correctassociativity(t);
+ break;
+ }
+ switch (numsiblings[t->tag]) {
+ case 1: /* finalfix(L, postable, g, sib1(t)); */
+ t = sib1(t); goto tailcall;
+ case 2:
+ finalfix(L, postable, g, sib1(t));
+ t = sib2(t); goto tailcall; /* finalfix(L, postable, g, sib2(t)); */
+ default: assert(numsiblings[t->tag] == 0); break;
+ }
+}
+
+
+
+/*
+** {===================================================================
+** KTable manipulation
+**
+** - The ktable of a pattern 'p' can be shared by other patterns that
+** contain 'p' and no other constants. Because of this sharing, we
+** should not add elements to a 'ktable' unless it was freshly created
+** for the new pattern.
+**
+** - The maximum index in a ktable is USHRT_MAX, because trees and
+** patterns use unsigned shorts to store those indices.
+** ====================================================================
+*/
+
+/*
+** Create a new 'ktable' to the pattern at the top of the stack.
+*/
+static void newktable (lua_State *L, int n) {
+ lua_createtable(L, n, 0); /* create a fresh table */
+ lua_setuservalue(L, -2); /* set it as 'ktable' for pattern */
+}
+
+
+/*
+** Add element 'idx' to 'ktable' of pattern at the top of the stack;
+** Return index of new element.
+** If new element is nil, does not add it to table (as it would be
+** useless) and returns 0, as ktable[0] is always nil.
+*/
+static int addtoktable (lua_State *L, int idx) {
+ if (lua_isnil(L, idx)) /* nil value? */
+ return 0;
+ else {
+ int n;
+ lua_getuservalue(L, -1); /* get ktable from pattern */
+ n = lua_rawlen(L, -1);
+ if (n >= USHRT_MAX)
+ luaL_error(L, "too many Lua values in pattern");
+ lua_pushvalue(L, idx); /* element to be added */
+ lua_rawseti(L, -2, ++n);
+ lua_pop(L, 1); /* remove 'ktable' */
+ return n;
+ }
+}
+
+
+/*
+** Return the number of elements in the ktable at 'idx'.
+** In Lua 5.2/5.3, default "environment" for patterns is nil, not
+** a table. Treat it as an empty table. In Lua 5.1, assumes that
+** the environment has no numeric indices (len == 0)
+*/
+static int ktablelen (lua_State *L, int idx) {
+ if (!lua_istable(L, idx)) return 0;
+ else return lua_rawlen(L, idx);
+}
+
+
+/*
+** Concatentate the contents of table 'idx1' into table 'idx2'.
+** (Assume that both indices are negative.)
+** Return the original length of table 'idx2' (or 0, if no
+** element was added, as there is no need to correct any index).
+*/
+static int concattable (lua_State *L, int idx1, int idx2) {
+ int i;
+ int n1 = ktablelen(L, idx1);
+ int n2 = ktablelen(L, idx2);
+ if (n1 + n2 > USHRT_MAX)
+ luaL_error(L, "too many Lua values in pattern");
+ if (n1 == 0) return 0; /* nothing to correct */
+ for (i = 1; i <= n1; i++) {
+ lua_rawgeti(L, idx1, i);
+ lua_rawseti(L, idx2 - 1, n2 + i); /* correct 'idx2' */
+ }
+ return n2;
+}
+
+
+/*
+** When joining 'ktables', constants from one of the subpatterns must
+** be renumbered; 'correctkeys' corrects their indices (adding 'n'
+** to each of them)
+*/
+static void correctkeys (TTree *tree, int n) {
+ if (n == 0) return; /* no correction? */
+ tailcall:
+ switch (tree->tag) {
+ case TOpenCall: case TCall: case TRunTime: case TRule: {
+ if (tree->key > 0)
+ tree->key += n;
+ break;
+ }
+ case TCapture: {
+ if (tree->key > 0 && tree->cap != Carg && tree->cap != Cnum)
+ tree->key += n;
+ break;
+ }
+ default: break;
+ }
+ switch (numsiblings[tree->tag]) {
+ case 1: /* correctkeys(sib1(tree), n); */
+ tree = sib1(tree); goto tailcall;
+ case 2:
+ correctkeys(sib1(tree), n);
+ tree = sib2(tree); goto tailcall; /* correctkeys(sib2(tree), n); */
+ default: assert(numsiblings[tree->tag] == 0); break;
+ }
+}
+
+
+/*
+** Join the ktables from p1 and p2 the ktable for the new pattern at the
+** top of the stack, reusing them when possible.
+*/
+static void joinktables (lua_State *L, int p1, TTree *t2, int p2) {
+ int n1, n2;
+ lua_getuservalue(L, p1); /* get ktables */
+ lua_getuservalue(L, p2);
+ n1 = ktablelen(L, -2);
+ n2 = ktablelen(L, -1);
+ if (n1 == 0 && n2 == 0) /* are both tables empty? */
+ lua_pop(L, 2); /* nothing to be done; pop tables */
+ else if (n2 == 0 || lp_equal(L, -2, -1)) { /* 2nd table empty or equal? */
+ lua_pop(L, 1); /* pop 2nd table */
+ lua_setuservalue(L, -2); /* set 1st ktable into new pattern */
+ }
+ else if (n1 == 0) { /* first table is empty? */
+ lua_setuservalue(L, -3); /* set 2nd table into new pattern */
+ lua_pop(L, 1); /* pop 1st table */
+ }
+ else {
+ lua_createtable(L, n1 + n2, 0); /* create ktable for new pattern */
+ /* stack: new p; ktable p1; ktable p2; new ktable */
+ concattable(L, -3, -1); /* from p1 into new ktable */
+ concattable(L, -2, -1); /* from p2 into new ktable */
+ lua_setuservalue(L, -4); /* new ktable becomes 'p' environment */
+ lua_pop(L, 2); /* pop other ktables */
+ correctkeys(t2, n1); /* correction for indices from p2 */
+ }
+}
+
+
+/*
+** copy 'ktable' of element 'idx' to new tree (on top of stack)
+*/
+static void copyktable (lua_State *L, int idx) {
+ lua_getuservalue(L, idx);
+ lua_setuservalue(L, -2);
+}
+
+
+/*
+** merge 'ktable' from 'stree' at stack index 'idx' into 'ktable'
+** from tree at the top of the stack, and correct corresponding
+** tree.
+*/
+static void mergektable (lua_State *L, int idx, TTree *stree) {
+ int n;
+ lua_getuservalue(L, -1); /* get ktables */
+ lua_getuservalue(L, idx);
+ n = concattable(L, -1, -2);
+ lua_pop(L, 2); /* remove both ktables */
+ correctkeys(stree, n);
+}
+
+
+/*
+** Create a new 'ktable' to the pattern at the top of the stack, adding
+** all elements from pattern 'p' (if not 0) plus element 'idx' to it.
+** Return index of new element.
+*/
+static int addtonewktable (lua_State *L, int p, int idx) {
+ newktable(L, 1);
+ if (p)
+ mergektable(L, p, NULL);
+ return addtoktable(L, idx);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Tree generation
+** =======================================================
+*/
+
+/*
+** In 5.2, could use 'luaL_testudata'...
+*/
+static int testpattern (lua_State *L, int idx) {
+ if (lua_touserdata(L, idx)) { /* value is a userdata? */
+ if (lua_getmetatable(L, idx)) { /* does it have a metatable? */
+ luaL_getmetatable(L, PATTERN_T);
+ if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
+ lua_pop(L, 2); /* remove both metatables */
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static Pattern *getpattern (lua_State *L, int idx) {
+ return (Pattern *)luaL_checkudata(L, idx, PATTERN_T);
+}
+
+
+static int getsize (lua_State *L, int idx) {
+ return (lua_rawlen(L, idx) - sizeof(Pattern)) / sizeof(TTree) + 1;
+}
+
+
+static TTree *gettree (lua_State *L, int idx, int *len) {
+ Pattern *p = getpattern(L, idx);
+ if (len)
+ *len = getsize(L, idx);
+ return p->tree;
+}
+
+
+/*
+** create a pattern. Set its uservalue (the 'ktable') equal to its
+** metatable. (It could be any empty sequence; the metatable is at
+** hand here, so we use it.)
+*/
+static TTree *newtree (lua_State *L, int len) {
+ size_t size = (len - 1) * sizeof(TTree) + sizeof(Pattern);
+ Pattern *p = (Pattern *)lua_newuserdata(L, size);
+ luaL_getmetatable(L, PATTERN_T);
+ lua_pushvalue(L, -1);
+ lua_setuservalue(L, -3);
+ lua_setmetatable(L, -2);
+ p->code = NULL; p->codesize = 0;
+ return p->tree;
+}
+
+
+static TTree *newleaf (lua_State *L, int tag) {
+ TTree *tree = newtree(L, 1);
+ tree->tag = tag;
+ return tree;
+}
+
+
+static TTree *newcharset (lua_State *L) {
+ TTree *tree = newtree(L, bytes2slots(CHARSETSIZE) + 1);
+ tree->tag = TSet;
+ loopset(i, treebuffer(tree)[i] = 0);
+ return tree;
+}
+
+
+/*
+** add to tree a sequence where first sibling is 'sib' (with size
+** 'sibsize'); returns position for second sibling
+*/
+static TTree *seqaux (TTree *tree, TTree *sib, int sibsize) {
+ tree->tag = TSeq; tree->u.ps = sibsize + 1;
+ memcpy(sib1(tree), sib, sibsize * sizeof(TTree));
+ return sib2(tree);
+}
+
+
+/*
+** Build a sequence of 'n' nodes, each with tag 'tag' and 'u.n' got
+** from the array 's' (or 0 if array is NULL). (TSeq is binary, so it
+** must build a sequence of sequence of sequence...)
+*/
+static void fillseq (TTree *tree, int tag, int n, const char *s) {
+ int i;
+ for (i = 0; i < n - 1; i++) { /* initial n-1 copies of Seq tag; Seq ... */
+ tree->tag = TSeq; tree->u.ps = 2;
+ sib1(tree)->tag = tag;
+ sib1(tree)->u.n = s ? (byte)s[i] : 0;
+ tree = sib2(tree);
+ }
+ tree->tag = tag; /* last one does not need TSeq */
+ tree->u.n = s ? (byte)s[i] : 0;
+}
+
+
+/*
+** Numbers as patterns:
+** 0 == true (always match); n == TAny repeated 'n' times;
+** -n == not (TAny repeated 'n' times)
+*/
+static TTree *numtree (lua_State *L, int n) {
+ if (n == 0)
+ return newleaf(L, TTrue);
+ else {
+ TTree *tree, *nd;
+ if (n > 0)
+ tree = nd = newtree(L, 2 * n - 1);
+ else { /* negative: code it as !(-n) */
+ n = -n;
+ tree = newtree(L, 2 * n);
+ tree->tag = TNot;
+ nd = sib1(tree);
+ }
+ fillseq(nd, TAny, n, NULL); /* sequence of 'n' any's */
+ return tree;
+ }
+}
+
+
+/*
+** Convert value at index 'idx' to a pattern
+*/
+static TTree *getpatt (lua_State *L, int idx, int *len) {
+ TTree *tree;
+ switch (lua_type(L, idx)) {
+ case LUA_TSTRING: {
+ size_t slen;
+ const char *s = lua_tolstring(L, idx, &slen); /* get string */
+ if (slen == 0) /* empty? */
+ tree = newleaf(L, TTrue); /* always match */
+ else {
+ tree = newtree(L, 2 * (slen - 1) + 1);
+ fillseq(tree, TChar, slen, s); /* sequence of 'slen' chars */
+ }
+ break;
+ }
+ case LUA_TNUMBER: {
+ int n = lua_tointeger(L, idx);
+ tree = numtree(L, n);
+ break;
+ }
+ case LUA_TBOOLEAN: {
+ tree = (lua_toboolean(L, idx) ? newleaf(L, TTrue) : newleaf(L, TFalse));
+ break;
+ }
+ case LUA_TTABLE: {
+ tree = newgrammar(L, idx);
+ break;
+ }
+ case LUA_TFUNCTION: {
+ tree = newtree(L, 2);
+ tree->tag = TRunTime;
+ tree->key = addtonewktable(L, 0, idx);
+ sib1(tree)->tag = TTrue;
+ break;
+ }
+ default: {
+ return gettree(L, idx, len);
+ }
+ }
+ lua_replace(L, idx); /* put new tree into 'idx' slot */
+ if (len)
+ *len = getsize(L, idx);
+ return tree;
+}
+
+
+/*
+** create a new tree, whith a new root and one sibling.
+** Sibling must be on the Lua stack, at index 1.
+*/
+static TTree *newroot1sib (lua_State *L, int tag) {
+ int s1;
+ TTree *tree1 = getpatt(L, 1, &s1);
+ TTree *tree = newtree(L, 1 + s1); /* create new tree */
+ tree->tag = tag;
+ memcpy(sib1(tree), tree1, s1 * sizeof(TTree));
+ copyktable(L, 1);
+ return tree;
+}
+
+
+/*
+** create a new tree, whith a new root and 2 siblings.
+** Siblings must be on the Lua stack, first one at index 1.
+*/
+static TTree *newroot2sib (lua_State *L, int tag) {
+ int s1, s2;
+ TTree *tree1 = getpatt(L, 1, &s1);
+ TTree *tree2 = getpatt(L, 2, &s2);
+ TTree *tree = newtree(L, 1 + s1 + s2); /* create new tree */
+ tree->tag = tag;
+ tree->u.ps = 1 + s1;
+ memcpy(sib1(tree), tree1, s1 * sizeof(TTree));
+ memcpy(sib2(tree), tree2, s2 * sizeof(TTree));
+ joinktables(L, 1, sib2(tree), 2);
+ return tree;
+}
+
+
+static int lp_P (lua_State *L) {
+ luaL_checkany(L, 1);
+ getpatt(L, 1, NULL);
+ lua_settop(L, 1);
+ return 1;
+}
+
+
+/*
+** sequence operator; optimizations:
+** false x => false, x true => x, true x => x
+** (cannot do x . false => false because x may have runtime captures)
+*/
+static int lp_seq (lua_State *L) {
+ TTree *tree1 = getpatt(L, 1, NULL);
+ TTree *tree2 = getpatt(L, 2, NULL);
+ if (tree1->tag == TFalse || tree2->tag == TTrue)
+ lua_pushvalue(L, 1); /* false . x == false, x . true = x */
+ else if (tree1->tag == TTrue)
+ lua_pushvalue(L, 2); /* true . x = x */
+ else
+ newroot2sib(L, TSeq);
+ return 1;
+}
+
+
+/*
+** choice operator; optimizations:
+** charset / charset => charset
+** true / x => true, x / false => x, false / x => x
+** (x / true is not equivalent to true)
+*/
+static int lp_choice (lua_State *L) {
+ Charset st1, st2;
+ TTree *t1 = getpatt(L, 1, NULL);
+ TTree *t2 = getpatt(L, 2, NULL);
+ if (tocharset(t1, &st1) && tocharset(t2, &st2)) {
+ TTree *t = newcharset(L);
+ loopset(i, treebuffer(t)[i] = st1.cs[i] | st2.cs[i]);
+ }
+ else if (nofail(t1) || t2->tag == TFalse)
+ lua_pushvalue(L, 1); /* true / x => true, x / false => x */
+ else if (t1->tag == TFalse)
+ lua_pushvalue(L, 2); /* false / x => x */
+ else
+ newroot2sib(L, TChoice);
+ return 1;
+}
+
+
+/*
+** p^n
+*/
+static int lp_star (lua_State *L) {
+ int size1;
+ int n = (int)luaL_checkinteger(L, 2);
+ TTree *tree1 = getpatt(L, 1, &size1);
+ if (n >= 0) { /* seq tree1 (seq tree1 ... (seq tree1 (rep tree1))) */
+ TTree *tree = newtree(L, (n + 1) * (size1 + 1));
+ if (nullable(tree1))
+ luaL_error(L, "loop body may accept empty string");
+ while (n--) /* repeat 'n' times */
+ tree = seqaux(tree, tree1, size1);
+ tree->tag = TRep;
+ memcpy(sib1(tree), tree1, size1 * sizeof(TTree));
+ }
+ else { /* choice (seq tree1 ... choice tree1 true ...) true */
+ TTree *tree;
+ n = -n;
+ /* size = (choice + seq + tree1 + true) * n, but the last has no seq */
+ tree = newtree(L, n * (size1 + 3) - 1);
+ for (; n > 1; n--) { /* repeat (n - 1) times */
+ tree->tag = TChoice; tree->u.ps = n * (size1 + 3) - 2;
+ sib2(tree)->tag = TTrue;
+ tree = sib1(tree);
+ tree = seqaux(tree, tree1, size1);
+ }
+ tree->tag = TChoice; tree->u.ps = size1 + 1;
+ sib2(tree)->tag = TTrue;
+ memcpy(sib1(tree), tree1, size1 * sizeof(TTree));
+ }
+ copyktable(L, 1);
+ return 1;
+}
+
+
+/*
+** #p == &p
+*/
+static int lp_and (lua_State *L) {
+ newroot1sib(L, TAnd);
+ return 1;
+}
+
+
+/*
+** -p == !p
+*/
+static int lp_not (lua_State *L) {
+ newroot1sib(L, TNot);
+ return 1;
+}
+
+
+/*
+** [t1 - t2] == Seq (Not t2) t1
+** If t1 and t2 are charsets, make their difference.
+*/
+static int lp_sub (lua_State *L) {
+ Charset st1, st2;
+ int s1, s2;
+ TTree *t1 = getpatt(L, 1, &s1);
+ TTree *t2 = getpatt(L, 2, &s2);
+ if (tocharset(t1, &st1) && tocharset(t2, &st2)) {
+ TTree *t = newcharset(L);
+ loopset(i, treebuffer(t)[i] = st1.cs[i] & ~st2.cs[i]);
+ }
+ else {
+ TTree *tree = newtree(L, 2 + s1 + s2);
+ tree->tag = TSeq; /* sequence of... */
+ tree->u.ps = 2 + s2;
+ sib1(tree)->tag = TNot; /* ...not... */
+ memcpy(sib1(sib1(tree)), t2, s2 * sizeof(TTree)); /* ...t2 */
+ memcpy(sib2(tree), t1, s1 * sizeof(TTree)); /* ... and t1 */
+ joinktables(L, 1, sib1(tree), 2);
+ }
+ return 1;
+}
+
+
+static int lp_set (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ TTree *tree = newcharset(L);
+ while (l--) {
+ setchar(treebuffer(tree), (byte)(*s));
+ s++;
+ }
+ return 1;
+}
+
+
+static int lp_range (lua_State *L) {
+ int arg;
+ int top = lua_gettop(L);
+ TTree *tree = newcharset(L);
+ for (arg = 1; arg <= top; arg++) {
+ int c;
+ size_t l;
+ const char *r = luaL_checklstring(L, arg, &l);
+ luaL_argcheck(L, l == 2, arg, "range must have two characters");
+ for (c = (byte)r[0]; c <= (byte)r[1]; c++)
+ setchar(treebuffer(tree), c);
+ }
+ return 1;
+}
+
+
+/*
+** Look-behind predicate
+*/
+static int lp_behind (lua_State *L) {
+ TTree *tree;
+ TTree *tree1 = getpatt(L, 1, NULL);
+ int n = fixedlen(tree1);
+ luaL_argcheck(L, n >= 0, 1, "pattern may not have fixed length");
+ luaL_argcheck(L, !hascaptures(tree1), 1, "pattern have captures");
+ luaL_argcheck(L, n <= MAXBEHIND, 1, "pattern too long to look behind");
+ tree = newroot1sib(L, TBehind);
+ tree->u.n = n;
+ return 1;
+}
+
+
+/*
+** Create a non-terminal
+*/
+static int lp_V (lua_State *L) {
+ TTree *tree = newleaf(L, TOpenCall);
+ luaL_argcheck(L, !lua_isnoneornil(L, 1), 1, "non-nil value expected");
+ tree->key = addtonewktable(L, 0, 1);
+ return 1;
+}
+
+
+/*
+** Create a tree for a non-empty capture, with a body and
+** optionally with an associated Lua value (at index 'labelidx' in the
+** stack)
+*/
+static int capture_aux (lua_State *L, int cap, int labelidx) {
+ TTree *tree = newroot1sib(L, TCapture);
+ tree->cap = cap;
+ tree->key = (labelidx == 0) ? 0 : addtonewktable(L, 1, labelidx);
+ return 1;
+}
+
+
+/*
+** Fill a tree with an empty capture, using an empty (TTrue) sibling.
+** (The 'key' field must be filled by the caller to finish the tree.)
+*/
+static TTree *auxemptycap (TTree *tree, int cap) {
+ tree->tag = TCapture;
+ tree->cap = cap;
+ sib1(tree)->tag = TTrue;
+ return tree;
+}
+
+
+/*
+** Create a tree for an empty capture.
+*/
+static TTree *newemptycap (lua_State *L, int cap, int key) {
+ TTree *tree = auxemptycap(newtree(L, 2), cap);
+ tree->key = key;
+ return tree;
+}
+
+
+/*
+** Create a tree for an empty capture with an associated Lua value.
+*/
+static TTree *newemptycapkey (lua_State *L, int cap, int idx) {
+ TTree *tree = auxemptycap(newtree(L, 2), cap);
+ tree->key = addtonewktable(L, 0, idx);
+ return tree;
+}
+
+
+/*
+** Captures with syntax p / v
+** (function capture, query capture, string capture, or number capture)
+*/
+static int lp_divcapture (lua_State *L) {
+ switch (lua_type(L, 2)) {
+ case LUA_TFUNCTION: return capture_aux(L, Cfunction, 2);
+ case LUA_TTABLE: return capture_aux(L, Cquery, 2);
+ case LUA_TSTRING: return capture_aux(L, Cstring, 2);
+ case LUA_TNUMBER: {
+ int n = lua_tointeger(L, 2);
+ TTree *tree = newroot1sib(L, TCapture);
+ luaL_argcheck(L, 0 <= n && n <= SHRT_MAX, 1, "invalid number");
+ tree->cap = Cnum;
+ tree->key = n;
+ return 1;
+ }
+ default: return luaL_argerror(L, 2, "invalid replacement value");
+ }
+}
+
+
+static int lp_substcapture (lua_State *L) {
+ return capture_aux(L, Csubst, 0);
+}
+
+
+static int lp_tablecapture (lua_State *L) {
+ return capture_aux(L, Ctable, 0);
+}
+
+
+static int lp_groupcapture (lua_State *L) {
+ if (lua_isnoneornil(L, 2))
+ return capture_aux(L, Cgroup, 0);
+ else
+ return capture_aux(L, Cgroup, 2);
+}
+
+
+static int lp_foldcapture (lua_State *L) {
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ return capture_aux(L, Cfold, 2);
+}
+
+
+static int lp_simplecapture (lua_State *L) {
+ return capture_aux(L, Csimple, 0);
+}
+
+
+static int lp_poscapture (lua_State *L) {
+ newemptycap(L, Cposition, 0);
+ return 1;
+}
+
+
+static int lp_argcapture (lua_State *L) {
+ int n = (int)luaL_checkinteger(L, 1);
+ luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, "invalid argument index");
+ newemptycap(L, Carg, n);
+ return 1;
+}
+
+
+static int lp_backref (lua_State *L) {
+ luaL_checkany(L, 1);
+ newemptycapkey(L, Cbackref, 1);
+ return 1;
+}
+
+
+/*
+** Constant capture
+*/
+static int lp_constcapture (lua_State *L) {
+ int i;
+ int n = lua_gettop(L); /* number of values */
+ if (n == 0) /* no values? */
+ newleaf(L, TTrue); /* no capture */
+ else if (n == 1)
+ newemptycapkey(L, Cconst, 1); /* single constant capture */
+ else { /* create a group capture with all values */
+ TTree *tree = newtree(L, 1 + 3 * (n - 1) + 2);
+ newktable(L, n); /* create a 'ktable' for new tree */
+ tree->tag = TCapture;
+ tree->cap = Cgroup;
+ tree->key = 0;
+ tree = sib1(tree);
+ for (i = 1; i <= n - 1; i++) {
+ tree->tag = TSeq;
+ tree->u.ps = 3; /* skip TCapture and its sibling */
+ auxemptycap(sib1(tree), Cconst);
+ sib1(tree)->key = addtoktable(L, i);
+ tree = sib2(tree);
+ }
+ auxemptycap(tree, Cconst);
+ tree->key = addtoktable(L, i);
+ }
+ return 1;
+}
+
+
+static int lp_matchtime (lua_State *L) {
+ TTree *tree;
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ tree = newroot1sib(L, TRunTime);
+ tree->key = addtonewktable(L, 1, 2);
+ return 1;
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Grammar - Tree generation
+** =======================================================
+*/
+
+/*
+** push on the stack the index and the pattern for the
+** initial rule of grammar at index 'arg' in the stack;
+** also add that index into position table.
+*/
+static void getfirstrule (lua_State *L, int arg, int postab) {
+ lua_rawgeti(L, arg, 1); /* access first element */
+ if (lua_isstring(L, -1)) { /* is it the name of initial rule? */
+ lua_pushvalue(L, -1); /* duplicate it to use as key */
+ lua_gettable(L, arg); /* get associated rule */
+ }
+ else {
+ lua_pushinteger(L, 1); /* key for initial rule */
+ lua_insert(L, -2); /* put it before rule */
+ }
+ if (!testpattern(L, -1)) { /* initial rule not a pattern? */
+ if (lua_isnil(L, -1))
+ luaL_error(L, "grammar has no initial rule");
+ else
+ luaL_error(L, "initial rule '%s' is not a pattern", lua_tostring(L, -2));
+ }
+ lua_pushvalue(L, -2); /* push key */
+ lua_pushinteger(L, 1); /* push rule position (after TGrammar) */
+ lua_settable(L, postab); /* insert pair at position table */
+}
+
+/*
+** traverse grammar at index 'arg', pushing all its keys and patterns
+** into the stack. Create a new table (before all pairs key-pattern) to
+** collect all keys and their associated positions in the final tree
+** (the "position table").
+** Return the number of rules and (in 'totalsize') the total size
+** for the new tree.
+*/
+static int collectrules (lua_State *L, int arg, int *totalsize) {
+ int n = 1; /* to count number of rules */
+ int postab = lua_gettop(L) + 1; /* index of position table */
+ int size; /* accumulator for total size */
+ lua_newtable(L); /* create position table */
+ getfirstrule(L, arg, postab);
+ size = 2 + getsize(L, postab + 2); /* TGrammar + TRule + rule */
+ lua_pushnil(L); /* prepare to traverse grammar table */
+ while (lua_next(L, arg) != 0) {
+ if (lua_tonumber(L, -2) == 1 ||
+ lp_equal(L, -2, postab + 1)) { /* initial rule? */
+ lua_pop(L, 1); /* remove value (keep key for lua_next) */
+ continue;
+ }
+ if (!testpattern(L, -1)) /* value is not a pattern? */
+ luaL_error(L, "rule '%s' is not a pattern", val2str(L, -2));
+ luaL_checkstack(L, LUA_MINSTACK, "grammar has too many rules");
+ lua_pushvalue(L, -2); /* push key (to insert into position table) */
+ lua_pushinteger(L, size);
+ lua_settable(L, postab);
+ size += 1 + getsize(L, -1); /* update size */
+ lua_pushvalue(L, -2); /* push key (for next lua_next) */
+ n++;
+ }
+ *totalsize = size + 1; /* TTrue to finish list of rules */
+ return n;
+}
+
+
+static void buildgrammar (lua_State *L, TTree *grammar, int frule, int n) {
+ int i;
+ TTree *nd = sib1(grammar); /* auxiliary pointer to traverse the tree */
+ for (i = 0; i < n; i++) { /* add each rule into new tree */
+ int ridx = frule + 2*i + 1; /* index of i-th rule */
+ int rulesize;
+ TTree *rn = gettree(L, ridx, &rulesize);
+ nd->tag = TRule;
+ nd->key = 0; /* will be fixed when rule is used */
+ nd->cap = i; /* rule number */
+ nd->u.ps = rulesize + 1; /* point to next rule */
+ memcpy(sib1(nd), rn, rulesize * sizeof(TTree)); /* copy rule */
+ mergektable(L, ridx, sib1(nd)); /* merge its ktable into new one */
+ nd = sib2(nd); /* move to next rule */
+ }
+ nd->tag = TTrue; /* finish list of rules */
+}
+
+
+/*
+** Check whether a tree has potential infinite loops
+*/
+static int checkloops (TTree *tree) {
+ tailcall:
+ if (tree->tag == TRep && nullable(sib1(tree)))
+ return 1;
+ else if (tree->tag == TGrammar)
+ return 0; /* sub-grammars already checked */
+ else {
+ switch (numsiblings[tree->tag]) {
+ case 1: /* return checkloops(sib1(tree)); */
+ tree = sib1(tree); goto tailcall;
+ case 2:
+ if (checkloops(sib1(tree))) return 1;
+ /* else return checkloops(sib2(tree)); */
+ tree = sib2(tree); goto tailcall;
+ default: assert(numsiblings[tree->tag] == 0); return 0;
+ }
+ }
+}
+
+
+/*
+** Give appropriate error message for 'verifyrule'. If a rule appears
+** twice in 'passed', there is path from it back to itself without
+** advancing the subject.
+*/
+static int verifyerror (lua_State *L, int *passed, int npassed) {
+ int i, j;
+ for (i = npassed - 1; i >= 0; i--) { /* search for a repetition */
+ for (j = i - 1; j >= 0; j--) {
+ if (passed[i] == passed[j]) {
+ lua_rawgeti(L, -1, passed[i]); /* get rule's key */
+ return luaL_error(L, "rule '%s' may be left recursive", val2str(L, -1));
+ }
+ }
+ }
+ return luaL_error(L, "too many left calls in grammar");
+}
+
+
+/*
+** Check whether a rule can be left recursive; raise an error in that
+** case; otherwise return 1 iff pattern is nullable.
+** The return value is used to check sequences, where the second pattern
+** is only relevant if the first is nullable.
+** Parameter 'nb' works as an accumulator, to allow tail calls in
+** choices. ('nb' true makes function returns true.)
+** Parameter 'passed' is a list of already visited rules, 'npassed'
+** counts the elements in 'passed'.
+** Assume ktable at the top of the stack.
+*/
+static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed,
+ int nb) {
+ tailcall:
+ switch (tree->tag) {
+ case TChar: case TSet: case TAny:
+ case TFalse:
+ return nb; /* cannot pass from here */
+ case TTrue:
+ case TBehind: /* look-behind cannot have calls */
+ return 1;
+ case TNot: case TAnd: case TRep:
+ /* return verifyrule(L, sib1(tree), passed, npassed, 1); */
+ tree = sib1(tree); nb = 1; goto tailcall;
+ case TCapture: case TRunTime:
+ /* return verifyrule(L, sib1(tree), passed, npassed, nb); */
+ tree = sib1(tree); goto tailcall;
+ case TCall:
+ /* return verifyrule(L, sib2(tree), passed, npassed, nb); */
+ tree = sib2(tree); goto tailcall;
+ case TSeq: /* only check 2nd child if first is nb */
+ if (!verifyrule(L, sib1(tree), passed, npassed, 0))
+ return nb;
+ /* else return verifyrule(L, sib2(tree), passed, npassed, nb); */
+ tree = sib2(tree); goto tailcall;
+ case TChoice: /* must check both children */
+ nb = verifyrule(L, sib1(tree), passed, npassed, nb);
+ /* return verifyrule(L, sib2(tree), passed, npassed, nb); */
+ tree = sib2(tree); goto tailcall;
+ case TRule:
+ if (npassed >= MAXRULES)
+ return verifyerror(L, passed, npassed);
+ else {
+ passed[npassed++] = tree->key;
+ /* return verifyrule(L, sib1(tree), passed, npassed); */
+ tree = sib1(tree); goto tailcall;
+ }
+ case TGrammar:
+ return nullable(tree); /* sub-grammar cannot be left recursive */
+ default: assert(0); return 0;
+ }
+}
+
+
+static void verifygrammar (lua_State *L, TTree *grammar) {
+ int passed[MAXRULES];
+ TTree *rule;
+ /* check left-recursive rules */
+ for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
+ if (rule->key == 0) continue; /* unused rule */
+ verifyrule(L, sib1(rule), passed, 0, 0);
+ }
+ assert(rule->tag == TTrue);
+ /* check infinite loops inside rules */
+ for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
+ if (rule->key == 0) continue; /* unused rule */
+ if (checkloops(sib1(rule))) {
+ lua_rawgeti(L, -1, rule->key); /* get rule's key */
+ luaL_error(L, "empty loop in rule '%s'", val2str(L, -1));
+ }
+ }
+ assert(rule->tag == TTrue);
+}
+
+
+/*
+** Give a name for the initial rule if it is not referenced
+*/
+static void initialrulename (lua_State *L, TTree *grammar, int frule) {
+ if (sib1(grammar)->key == 0) { /* initial rule is not referenced? */
+ int n = lua_rawlen(L, -1) + 1; /* index for name */
+ lua_pushvalue(L, frule); /* rule's name */
+ lua_rawseti(L, -2, n); /* ktable was on the top of the stack */
+ sib1(grammar)->key = n;
+ }
+}
+
+
+static TTree *newgrammar (lua_State *L, int arg) {
+ int treesize;
+ int frule = lua_gettop(L) + 2; /* position of first rule's key */
+ int n = collectrules(L, arg, &treesize);
+ TTree *g = newtree(L, treesize);
+ luaL_argcheck(L, n <= MAXRULES, arg, "grammar has too many rules");
+ g->tag = TGrammar; g->u.n = n;
+ lua_newtable(L); /* create 'ktable' */
+ lua_setuservalue(L, -2);
+ buildgrammar(L, g, frule, n);
+ lua_getuservalue(L, -1); /* get 'ktable' for new tree */
+ finalfix(L, frule - 1, g, sib1(g));
+ initialrulename(L, g, frule);
+ verifygrammar(L, g);
+ lua_pop(L, 1); /* remove 'ktable' */
+ lua_insert(L, -(n * 2 + 2)); /* move new table to proper position */
+ lua_pop(L, n * 2 + 1); /* remove position table + rule pairs */
+ return g; /* new table at the top of the stack */
+}
+
+/* }====================================================== */
+
+
+static Instruction *prepcompile (lua_State *L, Pattern *p, int idx) {
+ lua_getuservalue(L, idx); /* push 'ktable' (may be used by 'finalfix') */
+ finalfix(L, 0, NULL, p->tree);
+ lua_pop(L, 1); /* remove 'ktable' */
+ return compile(L, p);
+}
+
+
+static int lp_printtree (lua_State *L) {
+ TTree *tree = getpatt(L, 1, NULL);
+ int c = lua_toboolean(L, 2);
+ if (c) {
+ lua_getuservalue(L, 1); /* push 'ktable' (may be used by 'finalfix') */
+ finalfix(L, 0, NULL, tree);
+ lua_pop(L, 1); /* remove 'ktable' */
+ }
+ printktable(L, 1);
+ printtree(tree, 0);
+ return 0;
+}
+
+
+static int lp_printcode (lua_State *L) {
+ Pattern *p = getpattern(L, 1);
+ printktable(L, 1);
+ if (p->code == NULL) /* not compiled yet? */
+ prepcompile(L, p, 1);
+ printpatt(p->code, p->codesize);
+ return 0;
+}
+
+
+/*
+** Get the initial position for the match, interpreting negative
+** values from the end of the subject
+*/
+static size_t initposition (lua_State *L, size_t len) {
+ lua_Integer ii = luaL_optinteger(L, 3, 1);
+ if (ii > 0) { /* positive index? */
+ if ((size_t)ii <= len) /* inside the string? */
+ return (size_t)ii - 1; /* return it (corrected to 0-base) */
+ else return len; /* crop at the end */
+ }
+ else { /* negative index */
+ if ((size_t)(-ii) <= len) /* inside the string? */
+ return len - ((size_t)(-ii)); /* return position from the end */
+ else return 0; /* crop at the beginning */
+ }
+}
+
+
+/*
+** Main match function
+*/
+static int lp_match (lua_State *L) {
+ Capture capture[INITCAPSIZE];
+ const char *r;
+ size_t l;
+ Pattern *p = (getpatt(L, 1, NULL), getpattern(L, 1));
+ Instruction *code = (p->code != NULL) ? p->code : prepcompile(L, p, 1);
+ const char *s = luaL_checklstring(L, SUBJIDX, &l);
+ size_t i = initposition(L, l);
+ int ptop = lua_gettop(L);
+ lua_pushnil(L); /* initialize subscache */
+ lua_pushlightuserdata(L, capture); /* initialize caplistidx */
+ lua_getuservalue(L, 1); /* initialize penvidx */
+ r = match(L, s, s + i, s + l, code, capture, ptop);
+ if (r == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+ return getcaptures(L, s, r, ptop);
+}
+
+
+
+/*
+** {======================================================
+** Library creation and functions not related to matching
+** =======================================================
+*/
+
+/* maximum limit for stack size */
+#define MAXLIM (INT_MAX / 100)
+
+static int lp_setmax (lua_State *L) {
+ lua_Integer lim = luaL_checkinteger(L, 1);
+ luaL_argcheck(L, 0 < lim && lim <= MAXLIM, 1, "out of range");
+ lua_settop(L, 1);
+ lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
+ return 0;
+}
+
+
+static int lp_version (lua_State *L) {
+ lua_pushstring(L, VERSION);
+ return 1;
+}
+
+
+static int lp_type (lua_State *L) {
+ if (testpattern(L, 1))
+ lua_pushliteral(L, "pattern");
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+
+int lp_gc (lua_State *L) {
+ Pattern *p = getpattern(L, 1);
+ realloccode(L, p, 0); /* delete code block */
+ return 0;
+}
+
+
+static void createcat (lua_State *L, const char *catname, int (catf) (int)) {
+ TTree *t = newcharset(L);
+ int i;
+ for (i = 0; i <= UCHAR_MAX; i++)
+ if (catf(i)) setchar(treebuffer(t), i);
+ lua_setfield(L, -2, catname);
+}
+
+
+static int lp_locale (lua_State *L) {
+ if (lua_isnoneornil(L, 1)) {
+ lua_settop(L, 0);
+ lua_createtable(L, 0, 12);
+ }
+ else {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 1);
+ }
+ createcat(L, "alnum", isalnum);
+ createcat(L, "alpha", isalpha);
+ createcat(L, "cntrl", iscntrl);
+ createcat(L, "digit", isdigit);
+ createcat(L, "graph", isgraph);
+ createcat(L, "lower", islower);
+ createcat(L, "print", isprint);
+ createcat(L, "punct", ispunct);
+ createcat(L, "space", isspace);
+ createcat(L, "upper", isupper);
+ createcat(L, "xdigit", isxdigit);
+ return 1;
+}
+
+
+static struct luaL_Reg pattreg[] = {
+ {"ptree", lp_printtree},
+ {"pcode", lp_printcode},
+ {"match", lp_match},
+ {"B", lp_behind},
+ {"V", lp_V},
+ {"C", lp_simplecapture},
+ {"Cc", lp_constcapture},
+ {"Cmt", lp_matchtime},
+ {"Cb", lp_backref},
+ {"Carg", lp_argcapture},
+ {"Cp", lp_poscapture},
+ {"Cs", lp_substcapture},
+ {"Ct", lp_tablecapture},
+ {"Cf", lp_foldcapture},
+ {"Cg", lp_groupcapture},
+ {"P", lp_P},
+ {"S", lp_set},
+ {"R", lp_range},
+ {"locale", lp_locale},
+ {"version", lp_version},
+ {"setmaxstack", lp_setmax},
+ {"type", lp_type},
+ {NULL, NULL}
+};
+
+
+static struct luaL_Reg metareg[] = {
+ {"__mul", lp_seq},
+ {"__add", lp_choice},
+ {"__pow", lp_star},
+ {"__gc", lp_gc},
+ {"__len", lp_and},
+ {"__div", lp_divcapture},
+ {"__unm", lp_not},
+ {"__sub", lp_sub},
+ {NULL, NULL}
+};
+
+
+int luaopen_lpeg (lua_State *L);
+int luaopen_lpeg (lua_State *L) {
+ luaL_newmetatable(L, PATTERN_T);
+ lua_pushnumber(L, MAXBACK); /* initialize maximum backtracking */
+ lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
+ luaL_setfuncs(L, metareg, 0);
+ luaL_newlib(L, pattreg);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, "__index");
+ return 1;
+}
+
+/* }====================================================== */
diff --git a/luaclib/src/lpeg/lptree.h b/luaclib/src/lpeg/lptree.h
new file mode 100644
index 00000000..25906d5f
--- /dev/null
+++ b/luaclib/src/lpeg/lptree.h
@@ -0,0 +1,82 @@
+/*
+** $Id: lptree.h $
+*/
+
+#if !defined(lptree_h)
+#define lptree_h
+
+
+#include "lptypes.h"
+
+
+/*
+** types of trees
+*/
+typedef enum TTag {
+ TChar = 0, /* 'n' = char */
+ TSet, /* the set is stored in next CHARSETSIZE bytes */
+ TAny,
+ TTrue,
+ TFalse,
+ TRep, /* 'sib1'* */
+ TSeq, /* 'sib1' 'sib2' */
+ TChoice, /* 'sib1' / 'sib2' */
+ TNot, /* !'sib1' */
+ TAnd, /* &'sib1' */
+ TCall, /* ktable[key] is rule's key; 'sib2' is rule being called */
+ TOpenCall, /* ktable[key] is rule's key */
+ TRule, /* ktable[key] is rule's key (but key == 0 for unused rules);
+ 'sib1' is rule's pattern;
+ 'sib2' is next rule; 'cap' is rule's sequential number */
+ TGrammar, /* 'sib1' is initial (and first) rule */
+ TBehind, /* 'sib1' is pattern, 'n' is how much to go back */
+ TCapture, /* captures: 'cap' is kind of capture (enum 'CapKind');
+ ktable[key] is Lua value associated with capture;
+ 'sib1' is capture body */
+ TRunTime /* run-time capture: 'key' is Lua function;
+ 'sib1' is capture body */
+} TTag;
+
+
+/*
+** Tree trees
+** The first child of a tree (if there is one) is immediately after
+** the tree. A reference to a second child (ps) is its position
+** relative to the position of the tree itself.
+*/
+typedef struct TTree {
+ byte tag;
+ byte cap; /* kind of capture (if it is a capture) */
+ unsigned short key; /* key in ktable for Lua data (0 if no key) */
+ union {
+ int ps; /* occasional second child */
+ int n; /* occasional counter */
+ } u;
+} TTree;
+
+
+/*
+** A complete pattern has its tree plus, if already compiled,
+** its corresponding code
+*/
+typedef struct Pattern {
+ union Instruction *code;
+ int codesize;
+ TTree tree[1];
+} Pattern;
+
+
+/* number of children for each tree */
+extern const byte numsiblings[];
+
+/* access to children */
+#define sib1(t) ((t) + 1)
+#define sib2(t) ((t) + (t)->u.ps)
+
+
+
+
+
+
+#endif
+
diff --git a/luaclib/src/lpeg/lptypes.h b/luaclib/src/lpeg/lptypes.h
new file mode 100644
index 00000000..1d9d59f6
--- /dev/null
+++ b/luaclib/src/lpeg/lptypes.h
@@ -0,0 +1,145 @@
+/*
+** $Id: lptypes.h $
+** LPeg - PEG pattern matching for Lua
+** Copyright 2007-2019, Lua.org & PUC-Rio (see 'lpeg.html' for license)
+** written by Roberto Ierusalimschy
+*/
+
+#if !defined(lptypes_h)
+#define lptypes_h
+
+
+#include
+#include
+
+#include "lua.h"
+
+
+#define VERSION "1.0.2"
+
+
+#define PATTERN_T "lpeg-pattern"
+#define MAXSTACKIDX "lpeg-maxstack"
+
+
+/*
+** compatibility with Lua 5.1
+*/
+#if (LUA_VERSION_NUM == 501)
+
+#define lp_equal lua_equal
+
+#define lua_getuservalue lua_getfenv
+#define lua_setuservalue lua_setfenv
+
+#define lua_rawlen lua_objlen
+
+#define luaL_setfuncs(L,f,n) luaL_register(L,NULL,f)
+#define luaL_newlib(L,f) luaL_register(L,"lpeg",f)
+
+#endif
+
+
+#if !defined(lp_equal)
+#define lp_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
+#endif
+
+
+/* default maximum size for call/backtrack stack */
+#if !defined(MAXBACK)
+#define MAXBACK 400
+#endif
+
+
+/* maximum number of rules in a grammar (limited by 'unsigned char') */
+#if !defined(MAXRULES)
+#define MAXRULES 250
+#endif
+
+
+
+/* initial size for capture's list */
+#define INITCAPSIZE 32
+
+
+/* index, on Lua stack, for subject */
+#define SUBJIDX 2
+
+/* number of fixed arguments to 'match' (before capture arguments) */
+#define FIXEDARGS 3
+
+/* index, on Lua stack, for capture list */
+#define caplistidx(ptop) ((ptop) + 2)
+
+/* index, on Lua stack, for pattern's ktable */
+#define ktableidx(ptop) ((ptop) + 3)
+
+/* index, on Lua stack, for backtracking stack */
+#define stackidx(ptop) ((ptop) + 4)
+
+
+
+typedef unsigned char byte;
+
+
+#define BITSPERCHAR 8
+
+#define CHARSETSIZE ((UCHAR_MAX/BITSPERCHAR) + 1)
+
+
+
+typedef struct Charset {
+ byte cs[CHARSETSIZE];
+} Charset;
+
+
+
+#define loopset(v,b) { int v; for (v = 0; v < CHARSETSIZE; v++) {b;} }
+
+/* access to charset */
+#define treebuffer(t) ((byte *)((t) + 1))
+
+/* number of slots needed for 'n' bytes */
+#define bytes2slots(n) (((n) - 1) / sizeof(TTree) + 1)
+
+/* set 'b' bit in charset 'cs' */
+#define setchar(cs,b) ((cs)[(b) >> 3] |= (1 << ((b) & 7)))
+
+
+/*
+** in capture instructions, 'kind' of capture and its offset are
+** packed in field 'aux', 4 bits for each
+*/
+#define getkind(op) ((op)->i.aux & 0xF)
+#define getoff(op) (((op)->i.aux >> 4) & 0xF)
+#define joinkindoff(k,o) ((k) | ((o) << 4))
+
+#define MAXOFF 0xF
+#define MAXAUX 0xFF
+
+
+/* maximum number of bytes to look behind */
+#define MAXBEHIND MAXAUX
+
+
+/* maximum size (in elements) for a pattern */
+#define MAXPATTSIZE (SHRT_MAX - 10)
+
+
+/* size (in elements) for an instruction plus extra l bytes */
+#define instsize(l) (((l) + sizeof(Instruction) - 1)/sizeof(Instruction) + 1)
+
+
+/* size (in elements) for a ISet instruction */
+#define CHARSETINSTSIZE instsize(CHARSETSIZE)
+
+/* size (in elements) for a IFunc instruction */
+#define funcinstsize(p) ((p)->i.aux + 2)
+
+
+
+#define testchar(st,c) (((int)(st)[((c) >> 3)] & (1 << ((c) & 7))))
+
+
+#endif
+
diff --git a/luaclib/src/lpeg/lpvm.c b/luaclib/src/lpeg/lpvm.c
new file mode 100644
index 00000000..737418c4
--- /dev/null
+++ b/luaclib/src/lpeg/lpvm.c
@@ -0,0 +1,374 @@
+/*
+** $Id: lpvm.c $
+** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
+*/
+
+#include
+#include
+
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "lpcap.h"
+#include "lptypes.h"
+#include "lpvm.h"
+#include "lpprint.h"
+
+
+/* initial size for call/backtrack stack */
+#if !defined(INITBACK)
+#define INITBACK MAXBACK
+#endif
+
+
+#define getoffset(p) (((p) + 1)->offset)
+
+static const Instruction giveup = {{IGiveup, 0, 0}};
+
+
+/*
+** {======================================================
+** Virtual Machine
+** =======================================================
+*/
+
+
+typedef struct Stack {
+ const char *s; /* saved position (or NULL for calls) */
+ const Instruction *p; /* next instruction */
+ int caplevel;
+} Stack;
+
+
+#define getstackbase(L, ptop) ((Stack *)lua_touserdata(L, stackidx(ptop)))
+
+
+/*
+** Ensures the size of array 'capture' (with size '*capsize' and
+** 'captop' elements being used) is enough to accomodate 'n' extra
+** elements plus one. (Because several opcodes add stuff to the capture
+** array, it is simpler to ensure the array always has at least one free
+** slot upfront and check its size later.)
+*/
+static Capture *growcap (lua_State *L, Capture *capture, int *capsize,
+ int captop, int n, int ptop) {
+ if (*capsize - captop > n)
+ return capture; /* no need to grow array */
+ else { /* must grow */
+ Capture *newc;
+ int newsize = captop + n + 1; /* minimum size needed */
+ if (newsize < INT_MAX/((int)sizeof(Capture) * 2))
+ newsize *= 2; /* twice that size, if not too big */
+ else if (newsize >= INT_MAX/((int)sizeof(Capture)))
+ luaL_error(L, "too many captures");
+ newc = (Capture *)lua_newuserdata(L, newsize * sizeof(Capture));
+ memcpy(newc, capture, captop * sizeof(Capture));
+ *capsize = newsize;
+ lua_replace(L, caplistidx(ptop));
+ return newc;
+ }
+}
+
+
+/*
+** Double the size of the stack
+*/
+static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) {
+ Stack *stack = getstackbase(L, ptop);
+ Stack *newstack;
+ int n = *stacklimit - stack; /* current stack size */
+ int max, newn;
+ lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
+ max = lua_tointeger(L, -1); /* maximum allowed size */
+ lua_pop(L, 1);
+ if (n >= max) /* already at maximum size? */
+ luaL_error(L, "backtrack stack overflow (current limit is %d)", max);
+ newn = 2 * n; /* new size */
+ if (newn > max) newn = max;
+ newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack));
+ memcpy(newstack, stack, n * sizeof(Stack));
+ lua_replace(L, stackidx(ptop));
+ *stacklimit = newstack + newn;
+ return newstack + n; /* return next position */
+}
+
+
+/*
+** Interpret the result of a dynamic capture: false -> fail;
+** true -> keep current position; number -> next position.
+** Return new subject position. 'fr' is stack index where
+** is the result; 'curr' is current subject position; 'limit'
+** is subject's size.
+*/
+static int resdyncaptures (lua_State *L, int fr, int curr, int limit) {
+ lua_Integer res;
+ if (!lua_toboolean(L, fr)) { /* false value? */
+ lua_settop(L, fr - 1); /* remove results */
+ return -1; /* and fail */
+ }
+ else if (lua_isboolean(L, fr)) /* true? */
+ res = curr; /* keep current position */
+ else {
+ res = lua_tointeger(L, fr) - 1; /* new position */
+ if (res < curr || res > limit)
+ luaL_error(L, "invalid position returned by match-time capture");
+ }
+ lua_remove(L, fr); /* remove first result (offset) */
+ return res;
+}
+
+
+/*
+** Add capture values returned by a dynamic capture to the list
+** 'capture', nested inside a group. 'fd' indexes the first capture
+** value, 'n' is the number of values (at least 1). The open group
+** capture is already in 'capture', before the place for the new entries.
+*/
+static void adddyncaptures (const char *s, Capture *capture, int n, int fd) {
+ int i;
+ assert(capture[-1].kind == Cgroup && capture[-1].siz == 0);
+ capture[-1].idx = 0; /* make group capture an anonymous group */
+ for (i = 0; i < n; i++) { /* add runtime captures */
+ capture[i].kind = Cruntime;
+ capture[i].siz = 1; /* mark it as closed */
+ capture[i].idx = fd + i; /* stack index of capture value */
+ capture[i].s = s;
+ }
+ capture[n].kind = Cclose; /* close group */
+ capture[n].siz = 1;
+ capture[n].s = s;
+}
+
+
+/*
+** Remove dynamic captures from the Lua stack (called in case of failure)
+*/
+static int removedyncap (lua_State *L, Capture *capture,
+ int level, int last) {
+ int id = finddyncap(capture + level, capture + last); /* index of 1st cap. */
+ int top = lua_gettop(L);
+ if (id == 0) return 0; /* no dynamic captures? */
+ lua_settop(L, id - 1); /* remove captures */
+ return top - id + 1; /* number of values removed */
+}
+
+
+/*
+** Opcode interpreter
+*/
+const char *match (lua_State *L, const char *o, const char *s, const char *e,
+ Instruction *op, Capture *capture, int ptop) {
+ Stack stackbase[INITBACK];
+ Stack *stacklimit = stackbase + INITBACK;
+ Stack *stack = stackbase; /* point to first empty slot in stack */
+ int capsize = INITCAPSIZE;
+ int captop = 0; /* point to first empty slot in captures */
+ int ndyncap = 0; /* number of dynamic captures (in Lua stack) */
+ const Instruction *p = op; /* current instruction */
+ stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++;
+ lua_pushlightuserdata(L, stackbase);
+ for (;;) {
+#if defined(DEBUG)
+ printf("-------------------------------------\n");
+ printcaplist(capture, capture + captop);
+ printf("s: |%s| stck:%d, dyncaps:%d, caps:%d ",
+ s, (int)(stack - getstackbase(L, ptop)), ndyncap, captop);
+ printinst(op, p);
+#endif
+ assert(stackidx(ptop) + ndyncap == lua_gettop(L) && ndyncap <= captop);
+ switch ((Opcode)p->i.code) {
+ case IEnd: {
+ assert(stack == getstackbase(L, ptop) + 1);
+ capture[captop].kind = Cclose;
+ capture[captop].s = NULL;
+ return s;
+ }
+ case IGiveup: {
+ assert(stack == getstackbase(L, ptop));
+ return NULL;
+ }
+ case IRet: {
+ assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL);
+ p = (--stack)->p;
+ continue;
+ }
+ case IAny: {
+ if (s < e) { p++; s++; }
+ else goto fail;
+ continue;
+ }
+ case ITestAny: {
+ if (s < e) p += 2;
+ else p += getoffset(p);
+ continue;
+ }
+ case IChar: {
+ if ((byte)*s == p->i.aux && s < e) { p++; s++; }
+ else goto fail;
+ continue;
+ }
+ case ITestChar: {
+ if ((byte)*s == p->i.aux && s < e) p += 2;
+ else p += getoffset(p);
+ continue;
+ }
+ case ISet: {
+ int c = (byte)*s;
+ if (testchar((p+1)->buff, c) && s < e)
+ { p += CHARSETINSTSIZE; s++; }
+ else goto fail;
+ continue;
+ }
+ case ITestSet: {
+ int c = (byte)*s;
+ if (testchar((p + 2)->buff, c) && s < e)
+ p += 1 + CHARSETINSTSIZE;
+ else p += getoffset(p);
+ continue;
+ }
+ case IBehind: {
+ int n = p->i.aux;
+ if (n > s - o) goto fail;
+ s -= n; p++;
+ continue;
+ }
+ case ISpan: {
+ for (; s < e; s++) {
+ int c = (byte)*s;
+ if (!testchar((p+1)->buff, c)) break;
+ }
+ p += CHARSETINSTSIZE;
+ continue;
+ }
+ case IJmp: {
+ p += getoffset(p);
+ continue;
+ }
+ case IChoice: {
+ if (stack == stacklimit)
+ stack = doublestack(L, &stacklimit, ptop);
+ stack->p = p + getoffset(p);
+ stack->s = s;
+ stack->caplevel = captop;
+ stack++;
+ p += 2;
+ continue;
+ }
+ case ICall: {
+ if (stack == stacklimit)
+ stack = doublestack(L, &stacklimit, ptop);
+ stack->s = NULL;
+ stack->p = p + 2; /* save return address */
+ stack++;
+ p += getoffset(p);
+ continue;
+ }
+ case ICommit: {
+ assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
+ stack--;
+ p += getoffset(p);
+ continue;
+ }
+ case IPartialCommit: {
+ assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
+ (stack - 1)->s = s;
+ (stack - 1)->caplevel = captop;
+ p += getoffset(p);
+ continue;
+ }
+ case IBackCommit: {
+ assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
+ s = (--stack)->s;
+ captop = stack->caplevel;
+ p += getoffset(p);
+ continue;
+ }
+ case IFailTwice:
+ assert(stack > getstackbase(L, ptop));
+ stack--;
+ /* go through */
+ case IFail:
+ fail: { /* pattern failed: try to backtrack */
+ do { /* remove pending calls */
+ assert(stack > getstackbase(L, ptop));
+ s = (--stack)->s;
+ } while (s == NULL);
+ if (ndyncap > 0) /* is there matchtime captures? */
+ ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
+ captop = stack->caplevel;
+ p = stack->p;
+#if defined(DEBUG)
+ printf("**FAIL**\n");
+#endif
+ continue;
+ }
+ case ICloseRunTime: {
+ CapState cs;
+ int rem, res, n;
+ int fr = lua_gettop(L) + 1; /* stack index of first result */
+ cs.reclevel = 0; cs.L = L;
+ cs.s = o; cs.ocap = capture; cs.ptop = ptop;
+ n = runtimecap(&cs, capture + captop, s, &rem); /* call function */
+ captop -= n; /* remove nested captures */
+ ndyncap -= rem; /* update number of dynamic captures */
+ fr -= rem; /* 'rem' items were popped from Lua stack */
+ res = resdyncaptures(L, fr, s - o, e - o); /* get result */
+ if (res == -1) /* fail? */
+ goto fail;
+ s = o + res; /* else update current position */
+ n = lua_gettop(L) - fr + 1; /* number of new captures */
+ ndyncap += n; /* update number of dynamic captures */
+ if (n == 0) /* no new captures? */
+ captop--; /* remove open group */
+ else { /* new captures; keep original open group */
+ if (fr + n >= SHRT_MAX)
+ luaL_error(L, "too many results in match-time capture");
+ /* add new captures + close group to 'capture' list */
+ capture = growcap(L, capture, &capsize, captop, n + 1, ptop);
+ adddyncaptures(s, capture + captop, n, fr);
+ captop += n + 1; /* new captures + close group */
+ }
+ p++;
+ continue;
+ }
+ case ICloseCapture: {
+ const char *s1 = s;
+ assert(captop > 0);
+ /* if possible, turn capture into a full capture */
+ if (capture[captop - 1].siz == 0 &&
+ s1 - capture[captop - 1].s < UCHAR_MAX) {
+ capture[captop - 1].siz = s1 - capture[captop - 1].s + 1;
+ p++;
+ continue;
+ }
+ else {
+ capture[captop].siz = 1; /* mark entry as closed */
+ capture[captop].s = s;
+ goto pushcapture;
+ }
+ }
+ case IOpenCapture:
+ capture[captop].siz = 0; /* mark entry as open */
+ capture[captop].s = s;
+ goto pushcapture;
+ case IFullCapture:
+ capture[captop].siz = getoff(p) + 1; /* save capture size */
+ capture[captop].s = s - getoff(p);
+ /* goto pushcapture; */
+ pushcapture: {
+ capture[captop].idx = p->i.key;
+ capture[captop].kind = getkind(p);
+ captop++;
+ capture = growcap(L, capture, &capsize, captop, 0, ptop);
+ p++;
+ continue;
+ }
+ default: assert(0); return NULL;
+ }
+ }
+}
+
+/* }====================================================== */
+
+
diff --git a/luaclib/src/lpeg/lpvm.h b/luaclib/src/lpeg/lpvm.h
new file mode 100644
index 00000000..69ec33dc
--- /dev/null
+++ b/luaclib/src/lpeg/lpvm.h
@@ -0,0 +1,58 @@
+/*
+** $Id: lpvm.h $
+*/
+
+#if !defined(lpvm_h)
+#define lpvm_h
+
+#include "lpcap.h"
+
+
+/* Virtual Machine's instructions */
+typedef enum Opcode {
+ IAny, /* if no char, fail */
+ IChar, /* if char != aux, fail */
+ ISet, /* if char not in buff, fail */
+ ITestAny, /* in no char, jump to 'offset' */
+ ITestChar, /* if char != aux, jump to 'offset' */
+ ITestSet, /* if char not in buff, jump to 'offset' */
+ ISpan, /* read a span of chars in buff */
+ IBehind, /* walk back 'aux' characters (fail if not possible) */
+ IRet, /* return from a rule */
+ IEnd, /* end of pattern */
+ IChoice, /* stack a choice; next fail will jump to 'offset' */
+ IJmp, /* jump to 'offset' */
+ ICall, /* call rule at 'offset' */
+ IOpenCall, /* call rule number 'key' (must be closed to a ICall) */
+ ICommit, /* pop choice and jump to 'offset' */
+ IPartialCommit, /* update top choice to current position and jump */
+ IBackCommit, /* "fails" but jump to its own 'offset' */
+ IFailTwice, /* pop one choice and then fail */
+ IFail, /* go back to saved state on choice and jump to saved offset */
+ IGiveup, /* internal use */
+ IFullCapture, /* complete capture of last 'off' chars */
+ IOpenCapture, /* start a capture */
+ ICloseCapture,
+ ICloseRunTime
+} Opcode;
+
+
+
+typedef union Instruction {
+ struct Inst {
+ byte code;
+ byte aux;
+ short key;
+ } i;
+ int offset;
+ byte buff[1];
+} Instruction;
+
+
+void printpatt (Instruction *p, int n);
+const char *match (lua_State *L, const char *o, const char *s, const char *e,
+ Instruction *op, Capture *capture, int ptop);
+
+
+#endif
+
diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile
new file mode 100644
index 00000000..8a5d4061
--- /dev/null
+++ b/luaclib/src/lpeg/makefile
@@ -0,0 +1,66 @@
+LIBNAME = lpeg
+LUADIR = ../lua/
+
+COPT = -O2 -DNDEBUG
+# COPT = -g
+
+CWARNS = -Wall -Wextra -pedantic \
+ -Waggregate-return \
+ -Wcast-align \
+ -Wcast-qual \
+ -Wdisabled-optimization \
+ -Wpointer-arith \
+ -Wshadow \
+ -Wsign-compare \
+ -Wundef \
+ -Wwrite-strings \
+ -Wbad-function-cast \
+ -Wdeclaration-after-statement \
+ -Wmissing-prototypes \
+ -Wnested-externs \
+ -Wstrict-prototypes \
+# -Wunreachable-code \
+
+
+CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) -fPIC
+CC = gcc
+
+FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o
+
+# For Linux
+linux:
+ $(MAKE) lpeg.so "DLLFLAGS = -shared -fPIC"
+
+# For Mac OS
+macosx:
+ $(MAKE) lpeg.so "DLLFLAGS = -bundle -undefined dynamic_lookup"
+
+build: $(FILES)
+ CC -o lpeg.so $(FILES) -shared -fPIC -lcore
+ mv *.so ../../
+ rm *.o *.so
+
+
+rebuild: $(FILES)
+ CC -o lpeg.so $(FILES) -shared -fPIC -lcore
+ mv *.so ../../
+ rm *.o *.so
+
+
+lpeg.so: $(FILES)
+ env $(CC) $(DLLFLAGS) $(FILES) -o lpeg.so
+
+$(FILES): makefile
+
+test: test.lua re.lua lpeg.so
+ ./test.lua
+
+clean:
+ rm -f $(FILES) lpeg.so
+
+
+lpcap.o: lpcap.c lpcap.h lptypes.h
+lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h
+lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h
+lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h
+lpvm.o: lpvm.c lpcap.h lptypes.h lpvm.h lpprint.h lptree.h
diff --git a/luaclib/src/lpeg/re.html b/luaclib/src/lpeg/re.html
new file mode 100644
index 00000000..ad60d509
--- /dev/null
+++ b/luaclib/src/lpeg/re.html
@@ -0,0 +1,494 @@
+
+
+
+ LPeg.re - Regex syntax for LPEG
+
+
+
+
+
+
+
+
+The re module
+(provided by file re.lua in the distribution)
+supports a somewhat conventional regex syntax
+for pattern usage within LPeg.
+
+
+
+The next table summarizes re's syntax.
+A p represents an arbitrary pattern;
+num represents a number ([0-9]+);
+name represents an identifier
+([a-zA-Z][a-zA-Z0-9_]*).
+Constructions are listed in order of decreasing precedence.
+
+
Syntax
Description
+
( p )
grouping
+
'string'
literal string
+
"string"
literal string
+
[class]
character class
+
.
any character
+
%name
+
pattern defs[name] or a pre-defined pattern
+
name
non terminal
+
<name>
non terminal
+
{}
position capture
+
{ p }
simple capture
+
{: p :}
anonymous group capture
+
{:name: p :}
named group capture
+
{~ p ~}
substitution capture
+
{| p |}
table capture
+
=name
back reference
+
+
p ?
optional match
+
p *
zero or more repetitions
+
p +
one or more repetitions
+
p^num
exactly n repetitions
+
p^+num
+
at least n repetitions
+
p^-num
+
at most n repetitions
+
p -> 'string'
string capture
+
p -> "string"
string capture
+
p -> num
numbered capture
+
p -> name
function/query/string capture
+equivalent to p / defs[name]
+
p => name
match-time capture
+equivalent to lpeg.Cmt(p, defs[name])
+
p ~> name
fold capture
+equivalent to lpeg.Cf(p, defs[name])
+
& p
and predicate
+
! p
not predicate
+
p1 p2
concatenation
+
p1 / p2
ordered choice
+
(name <- p)+
grammar
+
+
+Any space appearing in a syntax description can be
+replaced by zero or more space characters and Lua-style comments
+(-- until end of line).
+
+
+
+Character classes define sets of characters.
+An initial ^ complements the resulting set.
+A range x-y includes in the set
+all characters with codes between the codes of x and y.
+A pre-defined class %name includes all
+characters of that class.
+A simple character includes itself in the set.
+The only special characters inside a class are ^
+(special only if it is the first character);
+]
+(can be included in the set as the first character,
+after the optional ^);
+% (special only if followed by a letter);
+and -
+(can be included in the set as the first or the last character).
+
+
+
+Currently the pre-defined classes are similar to those from the
+Lua's string library
+(%a for letters,
+%A for non letters, etc.).
+There is also a class %nl
+containing only the newline character,
+which is particularly handy for grammars written inside long strings,
+as long strings do not interpret escape sequences like \n.
+
+Compiles the given string and
+returns an equivalent LPeg pattern.
+The given string may define either an expression or a grammar.
+The optional defs table provides extra Lua values
+to be used by the pattern.
+
+
+
re.find (subject, pattern [, init])
+
+Searches the given pattern in the given subject.
+If it finds a match,
+returns the index where this occurrence starts and
+the index where it ends.
+Otherwise, returns nil.
+
+
+
+An optional numeric argument init makes the search
+starts at that position in the subject string.
+As usual in Lua libraries,
+a negative value counts from the end.
+
+
+
re.gsub (subject, pattern, replacement)
+
+Does a global substitution,
+replacing all occurrences of pattern
+in the given subject by replacement.
+
+
re.match (subject, pattern)
+
+Matches the given pattern against the given subject,
+returning all captures.
+
+
+
re.updatelocale ()
+
+Updates the pre-defined character classes to the current locale.
+
+The next code shows a simple complete Lua program using
+the re module:
+
+
+local re = require"re"
+
+-- find the position of the first numeral in a string
+print(re.find("the number 423 is odd", "[0-9]+")) --> 12 14
+
+-- returns all words in a string
+print(re.match("the number 423 is odd", "({%a+} / .)*"))
+--> the number is odd
+
+-- returns the first numeral in a string
+print(re.match("the number 423 is odd", "s <- {%d+} / . s"))
+--> 423
+
+print(re.gsub("hello World", "[aeiou]", "."))
+--> h.ll. W.rld
+
+
+
+
Balanced parentheses
+
+The following call will produce the same pattern produced by the
+Lua expression in the
+balanced parentheses example:
+
+This example shows a simple way to build an
+abstract syntax tree (AST) for a given grammar.
+To keep our example simple,
+let us consider the following grammar
+for lists of names:
+
+
+p = re.compile[[
+ listname <- (name s)*
+ name <- [a-z][a-z]*
+ s <- %s*
+]]
+
+
+Now, we will add captures to build a corresponding AST.
+As a first step, the pattern will build a table to
+represent each non terminal;
+terminals will be represented by their corresponding strings:
+
+Now, a match against "hi hello bye"
+results in the table
+{{"hi"}, {"hello"}, {"bye"}}.
+
+
+For such a simple grammar,
+this AST is more than enough;
+actually, the tables around each single name
+are already overkilling.
+More complex grammars,
+however, may need some more structure.
+Specifically,
+it would be useful if each table had
+a tag field telling what non terminal
+that table represents.
+We can add such a tag using
+named group captures:
+
+A text is a sequence of items,
+wherein we apply a substitution capture to expand any macros.
+An item is either a macro,
+any character different from parentheses,
+or a parenthesized expression.
+A macro argument (arg) is a sequence
+of items different from a comma.
+(Note that a comma may appear inside an item,
+e.g., inside a parenthesized expression.)
+Again we do a substitution capture to expand any macro
+in the argument before expanding the outer macro.
+args is a list of arguments separated by commas.
+Finally we define the macros.
+Each macro is a string substitution;
+it replaces the macro name and its arguments by its corresponding string,
+with each %n replaced by the n-th argument.
+
+
+
Patterns
+
+This example shows the complete syntax
+of patterns accepted by re.
+
+
+p = [=[
+
+pattern <- exp !.
+exp <- S (grammar / alternative)
+
+alternative <- seq ('/' S seq)*
+seq <- prefix*
+prefix <- '&' S prefix / '!' S prefix / suffix
+suffix <- primary S (([+*?]
+ / '^' [+-]? num
+ / '->' S (string / '{}' / name)
+ / '=>' S name) S)*
+
+primary <- '(' exp ')' / string / class / defined
+ / '{:' (name ':')? exp ':}'
+ / '=' name
+ / '{}'
+ / '{~' exp '~}'
+ / '{' exp '}'
+ / '.'
+ / name S !arrow
+ / '<' name '>' -- old-style non terminals
+
+grammar <- definition+
+definition <- name S arrow exp
+
+class <- '[' '^'? item (!']' item)* ']'
+item <- defined / range / .
+range <- . '-' [^]]
+
+S <- (%s / '--' [^%nl]*)* -- spaces and comments
+name <- [A-Za-z][A-Za-z0-9_]*
+arrow <- '<-'
+num <- [0-9]+
+string <- '"' [^"]* '"' / "'" [^']* "'"
+defined <- '%' name
+
+]=]
+
+print(re.match(p, p)) -- a self description must match itself
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"),
+to deal in the Software without restriction,
+including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software,
+and to permit persons to whom the Software is
+furnished to do so,
+subject to the following conditions:
+
+
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+
+
+
+
+
+
+
+
diff --git a/luaclib/src/lpeg/re.lua b/luaclib/src/lpeg/re.lua
new file mode 100644
index 00000000..3bb8af7d
--- /dev/null
+++ b/luaclib/src/lpeg/re.lua
@@ -0,0 +1,267 @@
+-- $Id: re.lua $
+
+-- imported functions and modules
+local tonumber, type, print, error = tonumber, type, print, error
+local setmetatable = setmetatable
+local m = require"lpeg"
+
+-- 'm' will be used to parse expressions, and 'mm' will be used to
+-- create expressions; that is, 're' runs on 'm', creating patterns
+-- on 'mm'
+local mm = m
+
+-- pattern's metatable
+local mt = getmetatable(mm.P(0))
+
+
+
+-- No more global accesses after this point
+local version = _VERSION
+if version == "Lua 5.2" then _ENV = nil end
+
+
+local any = m.P(1)
+
+
+-- Pre-defined names
+local Predef = { nl = m.P"\n" }
+
+
+local mem
+local fmem
+local gmem
+
+
+local function updatelocale ()
+ mm.locale(Predef)
+ Predef.a = Predef.alpha
+ Predef.c = Predef.cntrl
+ Predef.d = Predef.digit
+ Predef.g = Predef.graph
+ Predef.l = Predef.lower
+ Predef.p = Predef.punct
+ Predef.s = Predef.space
+ Predef.u = Predef.upper
+ Predef.w = Predef.alnum
+ Predef.x = Predef.xdigit
+ Predef.A = any - Predef.a
+ Predef.C = any - Predef.c
+ Predef.D = any - Predef.d
+ Predef.G = any - Predef.g
+ Predef.L = any - Predef.l
+ Predef.P = any - Predef.p
+ Predef.S = any - Predef.s
+ Predef.U = any - Predef.u
+ Predef.W = any - Predef.w
+ Predef.X = any - Predef.x
+ mem = {} -- restart memoization
+ fmem = {}
+ gmem = {}
+ local mt = {__mode = "v"}
+ setmetatable(mem, mt)
+ setmetatable(fmem, mt)
+ setmetatable(gmem, mt)
+end
+
+
+updatelocale()
+
+
+
+local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
+
+
+local function patt_error (s, i)
+ local msg = (#s < i + 20) and s:sub(i)
+ or s:sub(i,i+20) .. "..."
+ msg = ("pattern error near '%s'"):format(msg)
+ error(msg, 2)
+end
+
+local function mult (p, n)
+ local np = mm.P(true)
+ while n >= 1 do
+ if n%2 >= 1 then np = np * p end
+ p = p * p
+ n = n/2
+ end
+ return np
+end
+
+local function equalcap (s, i, c)
+ if type(c) ~= "string" then return nil end
+ local e = #c + i
+ if s:sub(i, e - 1) == c then return e else return nil end
+end
+
+
+local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
+
+local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0
+
+local arrow = S * "<-"
+
+local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
+
+name = m.C(name)
+
+
+-- a defined name only have meaning in a given environment
+local Def = name * m.Carg(1)
+
+
+local function getdef (id, defs)
+ local c = defs and defs[id]
+ if not c then error("undefined name: " .. id) end
+ return c
+end
+
+-- match a name and return a group of its corresponding definition
+-- and 'f' (to be folded in 'Suffix')
+local function defwithfunc (f)
+ return m.Cg(Def / getdef * m.Cc(f))
+end
+
+
+local num = m.C(m.R"09"^1) * S / tonumber
+
+local String = "'" * m.C((any - "'")^0) * "'" +
+ '"' * m.C((any - '"')^0) * '"'
+
+
+local defined = "%" * Def / function (c,Defs)
+ local cat = Defs and Defs[c] or Predef[c]
+ if not cat then error ("name '" .. c .. "' undefined") end
+ return cat
+end
+
+local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
+
+local item = (defined + Range + m.C(any)) / m.P
+
+local Class =
+ "["
+ * (m.C(m.P"^"^-1)) -- optional complement symbol
+ * m.Cf(item * (item - "]")^0, mt.__add) /
+ function (c, p) return c == "^" and any - p or p end
+ * "]"
+
+local function adddef (t, k, exp)
+ if t[k] then
+ error("'"..k.."' already defined as a rule")
+ else
+ t[k] = exp
+ end
+ return t
+end
+
+local function firstdef (n, r) return adddef({n}, n, r) end
+
+
+local function NT (n, b)
+ if not b then
+ error("rule '"..n.."' used outside a grammar")
+ else return mm.V(n)
+ end
+end
+
+
+local exp = m.P{ "Exp",
+ Exp = S * ( m.V"Grammar"
+ + m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) );
+ Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul)
+ * (#seq_follow + patt_error);
+ Prefix = "&" * S * m.V"Prefix" / mt.__len
+ + "!" * S * m.V"Prefix" / mt.__unm
+ + m.V"Suffix";
+ Suffix = m.Cf(m.V"Primary" * S *
+ ( ( m.P"+" * m.Cc(1, mt.__pow)
+ + m.P"*" * m.Cc(0, mt.__pow)
+ + m.P"?" * m.Cc(-1, mt.__pow)
+ + "^" * ( m.Cg(num * m.Cc(mult))
+ + m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow))
+ )
+ + "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div))
+ + m.P"{}" * m.Cc(nil, m.Ct)
+ + defwithfunc(mt.__div)
+ )
+ + "=>" * S * defwithfunc(m.Cmt)
+ + "~>" * S * defwithfunc(m.Cf)
+ ) * S
+ )^0, function (a,b,f) return f(a,b) end );
+ Primary = "(" * m.V"Exp" * ")"
+ + String / mm.P
+ + Class
+ + defined
+ + "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" /
+ function (n, p) return mm.Cg(p, n) end
+ + "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
+ + m.P"{}" / mm.Cp
+ + "{~" * m.V"Exp" * "~}" / mm.Cs
+ + "{|" * m.V"Exp" * "|}" / mm.Ct
+ + "{" * m.V"Exp" * "}" / mm.C
+ + m.P"." * m.Cc(any)
+ + (name * -arrow + "<" * name * ">") * m.Cb("G") / NT;
+ Definition = name * arrow * m.V"Exp";
+ Grammar = m.Cg(m.Cc(true), "G") *
+ m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0,
+ adddef) / mm.P
+}
+
+local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)
+
+
+local function compile (p, defs)
+ if mm.type(p) == "pattern" then return p end -- already compiled
+ local cp = pattern:match(p, 1, defs)
+ if not cp then error("incorrect pattern", 3) end
+ return cp
+end
+
+local function match (s, p, i)
+ local cp = mem[p]
+ if not cp then
+ cp = compile(p)
+ mem[p] = cp
+ end
+ return cp:match(s, i or 1)
+end
+
+local function find (s, p, i)
+ local cp = fmem[p]
+ if not cp then
+ cp = compile(p) / 0
+ cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
+ fmem[p] = cp
+ end
+ local i, e = cp:match(s, i or 1)
+ if i then return i, e - 1
+ else return i
+ end
+end
+
+local function gsub (s, p, rep)
+ local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
+ gmem[p] = g
+ local cp = g[rep]
+ if not cp then
+ cp = compile(p)
+ cp = mm.Cs((cp / rep + 1)^0)
+ g[rep] = cp
+ end
+ return cp:match(s)
+end
+
+
+-- exported names
+local re = {
+ compile = compile,
+ match = match,
+ find = find,
+ gsub = gsub,
+ updatelocale = updatelocale,
+}
+
+if version == "Lua 5.1" then _G.re = re end
+
+return re
diff --git a/luaclib/src/lpeg/test.lua b/luaclib/src/lpeg/test.lua
new file mode 100755
index 00000000..8f9f5745
--- /dev/null
+++ b/luaclib/src/lpeg/test.lua
@@ -0,0 +1,1523 @@
+#!/usr/bin/env lua
+
+-- $Id: test.lua $
+
+-- require"strict" -- just to be pedantic
+
+local m = require"lpeg"
+
+
+-- for general use
+local a, b, c, d, e, f, g, p, t
+
+
+-- compatibility with Lua 5.2
+local unpack = rawget(table, "unpack") or unpack
+local loadstring = rawget(_G, "loadstring") or load
+
+
+local any = m.P(1)
+local space = m.S" \t\n"^0
+
+local function checkeq (x, y, p)
+if p then print(x,y) end
+ if type(x) ~= "table" then assert(x == y)
+ else
+ for k,v in pairs(x) do checkeq(v, y[k], p) end
+ for k,v in pairs(y) do checkeq(v, x[k], p) end
+ end
+end
+
+
+local mt = getmetatable(m.P(1))
+
+
+local allchar = {}
+for i=0,255 do allchar[i + 1] = i end
+allchar = string.char(unpack(allchar))
+assert(#allchar == 256)
+
+local function cs2str (c)
+ return m.match(m.Cs((c + m.P(1)/"")^0), allchar)
+end
+
+local function eqcharset (c1, c2)
+ assert(cs2str(c1) == cs2str(c2))
+end
+
+
+print"General tests for LPeg library"
+
+assert(type(m.version()) == "string")
+print("version " .. m.version())
+assert(m.type("alo") ~= "pattern")
+assert(m.type(io.input) ~= "pattern")
+assert(m.type(m.P"alo") == "pattern")
+
+-- tests for some basic optimizations
+assert(m.match(m.P(false) + "a", "a") == 2)
+assert(m.match(m.P(true) + "a", "a") == 1)
+assert(m.match("a" + m.P(false), "b") == nil)
+assert(m.match("a" + m.P(true), "b") == 1)
+
+assert(m.match(m.P(false) * "a", "a") == nil)
+assert(m.match(m.P(true) * "a", "a") == 2)
+assert(m.match("a" * m.P(false), "a") == nil)
+assert(m.match("a" * m.P(true), "a") == 2)
+
+assert(m.match(#m.P(false) * "a", "a") == nil)
+assert(m.match(#m.P(true) * "a", "a") == 2)
+assert(m.match("a" * #m.P(false), "a") == nil)
+assert(m.match("a" * #m.P(true), "a") == 2)
+
+
+-- tests for locale
+do
+ assert(m.locale(m) == m)
+ local t = {}
+ assert(m.locale(t, m) == t)
+ local x = m.locale()
+ for n,v in pairs(x) do
+ assert(type(n) == "string")
+ eqcharset(v, m[n])
+ end
+end
+
+
+assert(m.match(3, "aaaa"))
+assert(m.match(4, "aaaa"))
+assert(not m.match(5, "aaaa"))
+assert(m.match(-3, "aa"))
+assert(not m.match(-3, "aaa"))
+assert(not m.match(-3, "aaaa"))
+assert(not m.match(-4, "aaaa"))
+assert(m.P(-5):match"aaaa")
+
+assert(m.match("a", "alo") == 2)
+assert(m.match("al", "alo") == 3)
+assert(not m.match("alu", "alo"))
+assert(m.match(true, "") == 1)
+
+local digit = m.S"0123456789"
+local upper = m.S"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+local lower = m.S"abcdefghijklmnopqrstuvwxyz"
+local letter = m.S"" + upper + lower
+local alpha = letter + digit + m.R()
+
+eqcharset(m.S"", m.P(false))
+eqcharset(upper, m.R("AZ"))
+eqcharset(lower, m.R("az"))
+eqcharset(upper + lower, m.R("AZ", "az"))
+eqcharset(upper + lower, m.R("AZ", "cz", "aa", "bb", "90"))
+eqcharset(digit, m.S"01234567" + "8" + "9")
+eqcharset(upper, letter - lower)
+eqcharset(m.S(""), m.R())
+assert(cs2str(m.S("")) == "")
+
+eqcharset(m.S"\0", "\0")
+eqcharset(m.S"\1\0\2", m.R"\0\2")
+eqcharset(m.S"\1\0\2", m.R"\1\2" + "\0")
+eqcharset(m.S"\1\0\2" - "\0", m.R"\1\2")
+
+local word = alpha^1 * (1 - alpha)^0
+
+assert((word^0 * -1):match"alo alo")
+assert(m.match(word^1 * -1, "alo alo"))
+assert(m.match(word^2 * -1, "alo alo"))
+assert(not m.match(word^3 * -1, "alo alo"))
+
+assert(not m.match(word^-1 * -1, "alo alo"))
+assert(m.match(word^-2 * -1, "alo alo"))
+assert(m.match(word^-3 * -1, "alo alo"))
+
+local eos = m.P(-1)
+
+assert(m.match(digit^0 * letter * digit * eos, "1298a1"))
+assert(not m.match(digit^0 * letter * eos, "1257a1"))
+
+b = {
+ [1] = "(" * (((1 - m.S"()") + #m.P"(" * m.V(1))^0) * ")"
+}
+
+assert(m.match(b, "(al())()"))
+assert(not m.match(b * eos, "(al())()"))
+assert(m.match(b * eos, "((al())()(é))"))
+assert(not m.match(b, "(al()()"))
+
+assert(not m.match(letter^1 - "for", "foreach"))
+assert(m.match(letter^1 - ("for" * eos), "foreach"))
+assert(not m.match(letter^1 - ("for" * eos), "for"))
+
+function basiclookfor (p)
+ return m.P {
+ [1] = p + (1 * m.V(1))
+ }
+end
+
+function caplookfor (p)
+ return basiclookfor(p:C())
+end
+
+assert(m.match(caplookfor(letter^1), " 4achou123...") == "achou")
+a = {m.match(caplookfor(letter^1)^0, " two words, one more ")}
+checkeq(a, {"two", "words", "one", "more"})
+
+assert(m.match( basiclookfor((#m.P(b) * 1) * m.Cp()), " ( (a)") == 7)
+
+a = {m.match(m.C(digit^1 * m.Cc"d") + m.C(letter^1 * m.Cc"l"), "123")}
+checkeq(a, {"123", "d"})
+
+-- bug in LPeg 0.12 (nil value does not create a 'ktable')
+assert(m.match(m.Cc(nil), "") == nil)
+
+a = {m.match(m.C(digit^1 * m.Cc"d") + m.C(letter^1 * m.Cc"l"), "abcd")}
+checkeq(a, {"abcd", "l"})
+
+a = {m.match(m.Cc(10,20,30) * 'a' * m.Cp(), 'aaa')}
+checkeq(a, {10,20,30,2})
+a = {m.match(m.Cp() * m.Cc(10,20,30) * 'a' * m.Cp(), 'aaa')}
+checkeq(a, {1,10,20,30,2})
+a = m.match(m.Ct(m.Cp() * m.Cc(10,20,30) * 'a' * m.Cp()), 'aaa')
+checkeq(a, {1,10,20,30,2})
+a = m.match(m.Ct(m.Cp() * m.Cc(7,8) * m.Cc(10,20,30) * 'a' * m.Cp()), 'aaa')
+checkeq(a, {1,7,8,10,20,30,2})
+a = {m.match(m.Cc() * m.Cc() * m.Cc(1) * m.Cc(2,3,4) * m.Cc() * 'a', 'aaa')}
+checkeq(a, {1,2,3,4})
+
+a = {m.match(m.Cp() * letter^1 * m.Cp(), "abcd")}
+checkeq(a, {1, 5})
+
+
+t = {m.match({[1] = m.C(m.C(1) * m.V(1) + -1)}, "abc")}
+checkeq(t, {"abc", "a", "bc", "b", "c", "c", ""})
+
+-- bug in 0.12 ('hascapture' did not check for captures inside a rule)
+do
+ local pat = m.P{
+ 'S';
+ S1 = m.C('abc') + 3,
+ S = #m.V('S1') -- rule has capture, but '#' must ignore it
+ }
+ assert(pat:match'abc' == 1)
+end
+
+
+-- bug: loop in 'hascaptures'
+do
+ local p = m.C(-m.P{m.P'x' * m.V(1) + m.P'y'})
+ assert(p:match("xxx") == "")
+end
+
+
+
+-- test for small capture boundary
+for i = 250,260 do
+ assert(#m.match(m.C(i), string.rep('a', i)) == i)
+ assert(#m.match(m.C(m.C(i)), string.rep('a', i)) == i)
+end
+
+-- tests for any*n and any*-n
+for n = 1, 550, 13 do
+ local x_1 = string.rep('x', n - 1)
+ local x = x_1 .. 'a'
+ assert(not m.P(n):match(x_1))
+ assert(m.P(n):match(x) == n + 1)
+ assert(n < 4 or m.match(m.P(n) + "xxx", x_1) == 4)
+ assert(m.C(n):match(x) == x)
+ assert(m.C(m.C(n)):match(x) == x)
+ assert(m.P(-n):match(x_1) == 1)
+ assert(not m.P(-n):match(x))
+ assert(n < 13 or m.match(m.Cc(20) * ((n - 13) * m.P(10)) * 3, x) == 20)
+ local n3 = math.floor(n/3)
+ assert(m.match(n3 * m.Cp() * n3 * n3, x) == n3 + 1)
+end
+
+-- true values
+assert(m.P(0):match("x") == 1)
+assert(m.P(0):match("") == 1)
+assert(m.C(0):match("x") == "")
+
+assert(m.match(m.Cc(0) * m.P(10) + m.Cc(1) * "xuxu", "xuxu") == 1)
+assert(m.match(m.Cc(0) * m.P(10) + m.Cc(1) * "xuxu", "xuxuxuxuxu") == 0)
+assert(m.match(m.C(m.P(2)^1), "abcde") == "abcd")
+p = m.Cc(0) * 1 + m.Cc(1) * 2 + m.Cc(2) * 3 + m.Cc(3) * 4
+
+
+-- test for alternation optimization
+assert(m.match(m.P"a"^1 + "ab" + m.P"x"^0, "ab") == 2)
+assert(m.match((m.P"a"^1 + "ab" + m.P"x"^0 * 1)^0, "ab") == 3)
+assert(m.match(m.P"ab" + "cd" + "" + "cy" + "ak", "98") == 1)
+assert(m.match(m.P"ab" + "cd" + "ax" + "cy", "ax") == 3)
+assert(m.match("a" * m.P"b"^0 * "c" + "cd" + "ax" + "cy", "ax") == 3)
+assert(m.match((m.P"ab" + "cd" + "ax" + "cy")^0, "ax") == 3)
+assert(m.match(m.P(1) * "x" + m.S"" * "xu" + "ay", "ay") == 3)
+assert(m.match(m.P"abc" + "cde" + "aka", "aka") == 4)
+assert(m.match(m.S"abc" * "x" + "cde" + "aka", "ax") == 3)
+assert(m.match(m.S"abc" * "x" + "cde" + "aka", "aka") == 4)
+assert(m.match(m.S"abc" * "x" + "cde" + "aka", "cde") == 4)
+assert(m.match(m.S"abc" * "x" + "ide" + m.S"ab" * "ka", "aka") == 4)
+assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "cde" + "aka", "ax") == 3)
+assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "cde" + "aka", "aka") == 4)
+assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "cde" + "aka", "cde") == 4)
+assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "ide" + m.S"ab" * "ka", "aka") == 4)
+assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "ide" + m.S"ab" * "ka", "ax") == 3)
+assert(m.match(m.P(1) * "x" + "cde" + m.S"ab" * "ka", "aka") == 4)
+assert(m.match(m.P(1) * "x" + "cde" + m.P(1) * "ka", "aka") == 4)
+assert(m.match(m.P(1) * "x" + "cde" + m.P(1) * "ka", "cde") == 4)
+assert(m.match(m.P"eb" + "cd" + m.P"e"^0 + "x", "ee") == 3)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x", "abcd") == 3)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x", "eeex") == 4)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x", "cd") == 3)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x", "x") == 1)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x" + "", "zee") == 1)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x", "abcd") == 3)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x", "eeex") == 4)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x", "cd") == 3)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x", "x") == 2)
+assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x" + "", "zee") == 1)
+assert(not m.match(("aa" * m.P"bc"^-1 + "aab") * "e", "aabe"))
+
+assert(m.match("alo" * (m.P"\n" + -1), "alo") == 4)
+
+
+-- bug in 0.12 (rc1)
+assert(m.match((m.P"\128\187\191" + m.S"abc")^0, "\128\187\191") == 4)
+
+assert(m.match(m.S"\0\128\255\127"^0, string.rep("\0\128\255\127", 10)) ==
+ 4*10 + 1)
+
+-- optimizations with optional parts
+assert(m.match(("ab" * -m.P"c")^-1, "abc") == 1)
+assert(m.match(("ab" * #m.P"c")^-1, "abd") == 1)
+assert(m.match(("ab" * m.B"c")^-1, "ab") == 1)
+assert(m.match(("ab" * m.P"cd"^0)^-1, "abcdcdc") == 7)
+
+assert(m.match(m.P"ab"^-1 - "c", "abcd") == 3)
+
+p = ('Aa' * ('Bb' * ('Cc' * m.P'Dd'^0)^0)^0)^-1
+assert(p:match("AaBbCcDdBbCcDdDdDdBb") == 21)
+
+
+-- bug in 0.12.2
+-- p = { ('ab' ('c' 'ef'?)*)? }
+p = m.C(('ab' * ('c' * m.P'ef'^-1)^0)^-1)
+s = "abcefccefc"
+assert(s == p:match(s))
+
+
+pi = "3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510"
+assert(m.match(m.Cs((m.P"1" / "a" + m.P"5" / "b" + m.P"9" / "c" + 1)^0), pi) ==
+ m.match(m.Cs((m.P(1) / {["1"] = "a", ["5"] = "b", ["9"] = "c"})^0), pi))
+print"+"
+
+
+-- tests for capture optimizations
+assert(m.match((m.P(3) + 4 * m.Cp()) * "a", "abca") == 5)
+t = {m.match(((m.P"a" + m.Cp()) * m.P"x")^0, "axxaxx")}
+checkeq(t, {3, 6})
+
+
+-- tests for numbered captures
+p = m.C(1)
+assert(m.match(m.C(m.C(p * m.C(2)) * m.C(3)) / 3, "abcdefgh") == "a")
+assert(m.match(m.C(m.C(p * m.C(2)) * m.C(3)) / 1, "abcdefgh") == "abcdef")
+assert(m.match(m.C(m.C(p * m.C(2)) * m.C(3)) / 4, "abcdefgh") == "bc")
+assert(m.match(m.C(m.C(p * m.C(2)) * m.C(3)) / 0, "abcdefgh") == 7)
+
+a, b, c = m.match(p * (m.C(p * m.C(2)) * m.C(3) / 4) * p, "abcdefgh")
+assert(a == "a" and b == "efg" and c == "h")
+
+-- test for table captures
+t = m.match(m.Ct(letter^1), "alo")
+checkeq(t, {})
+
+t, n = m.match(m.Ct(m.C(letter)^1) * m.Cc"t", "alo")
+assert(n == "t" and table.concat(t) == "alo")
+
+t = m.match(m.Ct(m.C(m.C(letter)^1)), "alo")
+assert(table.concat(t, ";") == "alo;a;l;o")
+
+t = m.match(m.Ct(m.C(m.C(letter)^1)), "alo")
+assert(table.concat(t, ";") == "alo;a;l;o")
+
+t = m.match(m.Ct(m.Ct((m.Cp() * letter * m.Cp())^1)), "alo")
+assert(table.concat(t[1], ";") == "1;2;2;3;3;4")
+
+t = m.match(m.Ct(m.C(m.C(1) * 1 * m.C(1))), "alo")
+checkeq(t, {"alo", "a", "o"})
+
+
+-- tests for groups
+p = m.Cg(1) -- no capture
+assert(p:match('x') == 'x')
+p = m.Cg(m.P(true)/function () end * 1) -- no value
+assert(p:match('x') == 'x')
+p = m.Cg(m.Cg(m.Cg(m.C(1))))
+assert(p:match('x') == 'x')
+p = m.Cg(m.Cg(m.Cg(m.C(1))^0) * m.Cg(m.Cc(1) * m.Cc(2)))
+t = {p:match'abc'}
+checkeq(t, {'a', 'b', 'c', 1, 2})
+
+p = m.Ct(m.Cg(m.Cc(10), "hi") * m.C(1)^0 * m.Cg(m.Cc(20), "ho"))
+t = p:match''
+checkeq(t, {hi = 10, ho = 20})
+t = p:match'abc'
+checkeq(t, {hi = 10, ho = 20, 'a', 'b', 'c'})
+
+-- non-string group names
+p = m.Ct(m.Cg(1, print) * m.Cg(1, 23.5) * m.Cg(1, io))
+t = p:match('abcdefghij')
+assert(t[print] == 'a' and t[23.5] == 'b' and t[io] == 'c')
+
+
+-- test for error messages
+local function checkerr (msg, f, ...)
+ local st, err = pcall(f, ...)
+ assert(not st and m.match({ m.P(msg) + 1 * m.V(1) }, err))
+end
+
+checkerr("rule '1' may be left recursive", m.match, { m.V(1) * 'a' }, "a")
+checkerr("rule '1' used outside a grammar", m.match, m.V(1), "")
+checkerr("rule 'hiii' used outside a grammar", m.match, m.V('hiii'), "")
+checkerr("rule 'hiii' undefined in given grammar", m.match, { m.V('hiii') }, "")
+checkerr("undefined in given grammar", m.match, { m.V{} }, "")
+
+checkerr("rule 'A' is not a pattern", m.P, { m.P(1), A = {} })
+checkerr("grammar has no initial rule", m.P, { [print] = {} })
+
+-- grammar with a long call chain before left recursion
+p = {'a',
+ a = m.V'b' * m.V'c' * m.V'd' * m.V'a',
+ b = m.V'c',
+ c = m.V'd',
+ d = m.V'e',
+ e = m.V'f',
+ f = m.V'g',
+ g = m.P''
+}
+checkerr("rule 'a' may be left recursive", m.match, p, "a")
+
+-- Bug in peephole optimization of LPeg 0.12 (IJmp -> ICommit)
+-- the next grammar has an original sequence IJmp -> ICommit -> IJmp L1
+-- that is optimized to ICommit L1
+
+p = m.P { (m.P {m.P'abc'} + 'ayz') * m.V'y'; y = m.P'x' }
+assert(p:match('abcx') == 5 and p:match('ayzx') == 5 and not p:match'abc')
+
+
+do
+ -- large dynamic Cc
+ local lim = 2^16 - 1
+ local c = 0
+ local function seq (n)
+ if n == 1 then c = c + 1; return m.Cc(c)
+ else
+ local m = math.floor(n / 2)
+ return seq(m) * seq(n - m)
+ end
+ end
+ p = m.Ct(seq(lim))
+ t = p:match('')
+ assert(t[lim] == lim)
+ checkerr("too many", function () p = p / print end)
+ checkerr("too many", seq, lim + 1)
+end
+
+
+do
+ -- nesting of captures too deep
+ local p = m.C(1)
+ for i = 1, 300 do
+ p = m.Ct(p)
+ end
+ checkerr("too deep", p.match, p, "x")
+end
+
+
+-- tests for non-pattern as arguments to pattern functions
+
+p = { ('a' * m.V(1))^-1 } * m.P'b' * { 'a' * m.V(2); m.V(1)^-1 }
+assert(m.match(p, "aaabaac") == 7)
+
+p = m.P'abc' * 2 * -5 * true * 'de' -- mix of numbers and strings and booleans
+
+assert(p:match("abc01de") == 8)
+assert(p:match("abc01de3456") == nil)
+
+p = 'abc' * (2 * (-5 * (true * m.P'de')))
+
+assert(p:match("abc01de") == 8)
+assert(p:match("abc01de3456") == nil)
+
+p = { m.V(2), m.P"abc" } *
+ (m.P{ "xx", xx = m.P"xx" } + { "x", x = m.P"a" * m.V"x" + "" })
+assert(p:match("abcaaaxx") == 7)
+assert(p:match("abcxx") == 6)
+
+
+-- a large table capture
+t = m.match(m.Ct(m.C('a')^0), string.rep("a", 10000))
+assert(#t == 10000 and t[1] == 'a' and t[#t] == 'a')
+
+print('+')
+
+
+-- bug in 0.10 (rechecking a grammar, after tail-call optimization)
+m.P{ m.P { (m.P(3) + "xuxu")^0 * m.V"xuxu", xuxu = m.P(1) } }
+
+local V = m.V
+
+local Space = m.S(" \n\t")^0
+local Number = m.C(m.R("09")^1) * Space
+local FactorOp = m.C(m.S("+-")) * Space
+local TermOp = m.C(m.S("*/")) * Space
+local Open = "(" * Space
+local Close = ")" * Space
+
+
+local function f_factor (v1, op, v2, d)
+ assert(d == nil)
+ if op == "+" then return v1 + v2
+ else return v1 - v2
+ end
+end
+
+
+local function f_term (v1, op, v2, d)
+ assert(d == nil)
+ if op == "*" then return v1 * v2
+ else return v1 / v2
+ end
+end
+
+G = m.P{ "Exp",
+ Exp = m.Cf(V"Factor" * m.Cg(FactorOp * V"Factor")^0, f_factor);
+ Factor = m.Cf(V"Term" * m.Cg(TermOp * V"Term")^0, f_term);
+ Term = Number / tonumber + Open * V"Exp" * Close;
+}
+
+G = Space * G * -1
+
+for _, s in ipairs{" 3 + 5*9 / (1+1) ", "3+4/2", "3+3-3- 9*2+3*9/1- 8"} do
+ assert(m.match(G, s) == loadstring("return "..s)())
+end
+
+
+-- test for grammars (errors deep in calling non-terminals)
+g = m.P{
+ [1] = m.V(2) + "a",
+ [2] = "a" * m.V(3) * "x",
+ [3] = "b" * m.V(3) + "c"
+}
+
+assert(m.match(g, "abbbcx") == 7)
+assert(m.match(g, "abbbbx") == 2)
+
+
+-- tests for \0
+assert(m.match(m.R("\0\1")^1, "\0\1\0") == 4)
+assert(m.match(m.S("\0\1ab")^1, "\0\1\0a") == 5)
+assert(m.match(m.P(1)^3, "\0\1\0a") == 5)
+assert(not m.match(-4, "\0\1\0a"))
+assert(m.match("\0\1\0a", "\0\1\0a") == 5)
+assert(m.match("\0\0\0", "\0\0\0") == 4)
+assert(not m.match("\0\0\0", "\0\0"))
+
+
+-- tests for predicates
+assert(not m.match(-m.P("a") * 2, "alo"))
+assert(m.match(- -m.P("a") * 2, "alo") == 3)
+assert(m.match(#m.P("a") * 2, "alo") == 3)
+assert(m.match(##m.P("a") * 2, "alo") == 3)
+assert(not m.match(##m.P("c") * 2, "alo"))
+assert(m.match(m.Cs((##m.P("a") * 1 + m.P(1)/".")^0), "aloal") == "a..a.")
+assert(m.match(m.Cs((#((#m.P"a")/"") * 1 + m.P(1)/".")^0), "aloal") == "a..a.")
+assert(m.match(m.Cs((- -m.P("a") * 1 + m.P(1)/".")^0), "aloal") == "a..a.")
+assert(m.match(m.Cs((-((-m.P"a")/"") * 1 + m.P(1)/".")^0), "aloal") == "a..a.")
+
+
+-- fixed length
+do
+ -- 'and' predicate using fixed length
+ local p = m.C(#("a" * (m.P("bd") + "cd")) * 2)
+ assert(p:match("acd") == "ac")
+
+ p = #m.P{ "a" * m.V(2), m.P"b" } * 2
+ assert(p:match("abc") == 3)
+
+ p = #(m.P"abc" * m.B"c")
+ assert(p:match("abc") == 1 and not p:match("ab"))
+
+ p = m.P{ "a" * m.V(2), m.P"b"^1 }
+ checkerr("pattern may not have fixed length", m.B, p)
+
+ p = "abc" * (m.P"b"^1 + m.P"a"^0)
+ checkerr("pattern may not have fixed length", m.B, p)
+end
+
+
+p = -m.P'a' * m.Cc(1) + -m.P'b' * m.Cc(2) + -m.P'c' * m.Cc(3)
+assert(p:match('a') == 2 and p:match('') == 1 and p:match('b') == 1)
+
+p = -m.P'a' * m.Cc(10) + #m.P'a' * m.Cc(20)
+assert(p:match('a') == 20 and p:match('') == 10 and p:match('b') == 10)
+
+
+
+-- look-behind predicate
+assert(not m.match(m.B'a', 'a'))
+assert(m.match(1 * m.B'a', 'a') == 2)
+assert(not m.match(m.B(1), 'a'))
+assert(m.match(1 * m.B(1), 'a') == 2)
+assert(m.match(-m.B(1), 'a') == 1)
+assert(m.match(m.B(250), string.rep('a', 250)) == nil)
+assert(m.match(250 * m.B(250), string.rep('a', 250)) == 251)
+
+-- look-behind with an open call
+checkerr("pattern may not have fixed length", m.B, m.V'S1')
+checkerr("too long to look behind", m.B, 260)
+
+B = #letter * -m.B(letter) + -letter * m.B(letter)
+x = m.Ct({ (B * m.Cp())^-1 * (1 * m.V(1) + m.P(true)) })
+checkeq(m.match(x, 'ar cal c'), {1,3,4,7,9,10})
+checkeq(m.match(x, ' ar cal '), {2,4,5,8})
+checkeq(m.match(x, ' '), {})
+checkeq(m.match(x, 'aloalo'), {1,7})
+
+assert(m.match(B, "a") == 1)
+assert(m.match(1 * B, "a") == 2)
+assert(not m.B(1 - letter):match(""))
+assert((-m.B(letter)):match("") == 1)
+
+assert((4 * m.B(letter, 4)):match("aaaaaaaa") == 5)
+assert(not (4 * m.B(#letter * 5)):match("aaaaaaaa"))
+assert((4 * -m.B(#letter * 5)):match("aaaaaaaa") == 5)
+
+-- look-behind with grammars
+assert(m.match('a' * m.B{'x', x = m.P(3)}, 'aaa') == nil)
+assert(m.match('aa' * m.B{'x', x = m.P('aaa')}, 'aaaa') == nil)
+assert(m.match('aaa' * m.B{'x', x = m.P('aaa')}, 'aaaaa') == 4)
+
+
+
+-- bug in 0.9
+assert(m.match(('a' * #m.P'b'), "ab") == 2)
+assert(not m.match(('a' * #m.P'b'), "a"))
+
+assert(not m.match(#m.S'567', ""))
+assert(m.match(#m.S'567' * 1, "6") == 2)
+
+
+-- tests for Tail Calls
+
+p = m.P{ 'a' * m.V(1) + '' }
+assert(p:match(string.rep('a', 1000)) == 1001)
+
+-- create a grammar for a simple DFA for even number of 0s and 1s
+--
+-- ->1 <---0---> 2
+-- ^ ^
+-- | |
+-- 1 1
+-- | |
+-- V V
+-- 3 <---0---> 4
+--
+-- this grammar should keep no backtracking information
+
+p = m.P{
+ [1] = '0' * m.V(2) + '1' * m.V(3) + -1,
+ [2] = '0' * m.V(1) + '1' * m.V(4),
+ [3] = '0' * m.V(4) + '1' * m.V(1),
+ [4] = '0' * m.V(3) + '1' * m.V(2),
+}
+
+assert(p:match(string.rep("00", 10000)))
+assert(p:match(string.rep("01", 10000)))
+assert(p:match(string.rep("011", 10000)))
+assert(not p:match(string.rep("011", 10000) .. "1"))
+assert(not p:match(string.rep("011", 10001)))
+
+
+-- this grammar does need backtracking info.
+local lim = 10000
+p = m.P{ '0' * m.V(1) + '0' }
+checkerr("stack overflow", m.match, p, string.rep("0", lim))
+m.setmaxstack(2*lim)
+checkerr("stack overflow", m.match, p, string.rep("0", lim))
+m.setmaxstack(2*lim + 4)
+assert(m.match(p, string.rep("0", lim)) == lim + 1)
+
+-- this repetition should not need stack space (only the call does)
+p = m.P{ ('a' * m.V(1))^0 * 'b' + 'c' }
+m.setmaxstack(200)
+assert(p:match(string.rep('a', 180) .. 'c' .. string.rep('b', 180)) == 362)
+
+m.setmaxstack(100) -- restore low limit
+
+-- tests for optional start position
+assert(m.match("a", "abc", 1))
+assert(m.match("b", "abc", 2))
+assert(m.match("c", "abc", 3))
+assert(not m.match(1, "abc", 4))
+assert(m.match("a", "abc", -3))
+assert(m.match("b", "abc", -2))
+assert(m.match("c", "abc", -1))
+assert(m.match("abc", "abc", -4)) -- truncate to position 1
+
+assert(m.match("", "abc", 10)) -- empty string is everywhere!
+assert(m.match("", "", 10))
+assert(not m.match(1, "", 1))
+assert(not m.match(1, "", -1))
+assert(not m.match(1, "", 0))
+
+print("+")
+
+
+-- tests for argument captures
+checkerr("invalid argument", m.Carg, 0)
+checkerr("invalid argument", m.Carg, -1)
+checkerr("invalid argument", m.Carg, 2^18)
+checkerr("absent extra argument #1", m.match, m.Carg(1), 'a', 1)
+assert(m.match(m.Carg(1), 'a', 1, print) == print)
+x = {m.match(m.Carg(1) * m.Carg(2), '', 1, 10, 20)}
+checkeq(x, {10, 20})
+
+assert(m.match(m.Cmt(m.Cg(m.Carg(3), "a") *
+ m.Cmt(m.Cb("a"), function (s,i,x)
+ assert(s == "a" and i == 1);
+ return i, x+1
+ end) *
+ m.Carg(2), function (s,i,a,b,c)
+ assert(s == "a" and i == 1 and c == nil);
+ return i, 2*a + 3*b
+ end) * "a",
+ "a", 1, false, 100, 1000) == 2*1001 + 3*100)
+
+
+-- tests for Lua functions
+
+t = {}
+s = ""
+p = m.P(function (s1, i) assert(s == s1); t[#t + 1] = i; return nil end) * false
+s = "hi, this is a test"
+assert(m.match(((p - m.P(-1)) + 2)^0, s) == string.len(s) + 1)
+assert(#t == string.len(s)/2 and t[1] == 1 and t[2] == 3)
+
+assert(not m.match(p, s))
+
+p = mt.__add(function (s, i) return i end, function (s, i) return nil end)
+assert(m.match(p, "alo"))
+
+p = mt.__mul(function (s, i) return i end, function (s, i) return nil end)
+assert(not m.match(p, "alo"))
+
+
+t = {}
+p = function (s1, i) assert(s == s1); t[#t + 1] = i; return i end
+s = "hi, this is a test"
+assert(m.match((m.P(1) * p)^0, s) == string.len(s) + 1)
+assert(#t == string.len(s) and t[1] == 2 and t[2] == 3)
+
+t = {}
+p = m.P(function (s1, i) assert(s == s1); t[#t + 1] = i;
+ return i <= s1:len() and i end) * 1
+s = "hi, this is a test"
+assert(m.match(p^0, s) == string.len(s) + 1)
+assert(#t == string.len(s) + 1 and t[1] == 1 and t[2] == 2)
+
+p = function (s1, i) return m.match(m.P"a"^1, s1, i) end
+assert(m.match(p, "aaaa") == 5)
+assert(m.match(p, "abaa") == 2)
+assert(not m.match(p, "baaa"))
+
+checkerr("invalid position", m.match, function () return 2^20 end, s)
+checkerr("invalid position", m.match, function () return 0 end, s)
+checkerr("invalid position", m.match, function (s, i) return i - 1 end, s)
+checkerr("invalid position", m.match,
+ m.P(1)^0 * function (_, i) return i - 1 end, s)
+assert(m.match(m.P(1)^0 * function (_, i) return i end * -1, s))
+checkerr("invalid position", m.match,
+ m.P(1)^0 * function (_, i) return i + 1 end, s)
+assert(m.match(m.P(function (s, i) return s:len() + 1 end) * -1, s))
+checkerr("invalid position", m.match, m.P(function (s, i) return s:len() + 2 end) * -1, s)
+assert(not m.match(m.P(function (s, i) return s:len() end) * -1, s))
+assert(m.match(m.P(1)^0 * function (_, i) return true end, s) ==
+ string.len(s) + 1)
+for i = 1, string.len(s) + 1 do
+ assert(m.match(function (_, _) return i end, s) == i)
+end
+
+p = (m.P(function (s, i) return i%2 == 0 and i end) * 1
+ + m.P(function (s, i) return i%2 ~= 0 and i + 2 <= s:len() and i end) * 3)^0
+ * -1
+assert(p:match(string.rep('a', 14000)))
+
+-- tests for Function Replacements
+f = function (a, ...) if a ~= "x" then return {a, ...} end end
+
+t = m.match(m.C(1)^0/f, "abc")
+checkeq(t, {"a", "b", "c"})
+
+t = m.match(m.C(1)^0/f/f, "abc")
+checkeq(t, {{"a", "b", "c"}})
+
+t = m.match(m.P(1)^0/f/f, "abc") -- no capture
+checkeq(t, {{"abc"}})
+
+t = m.match((m.P(1)^0/f * m.Cp())/f, "abc")
+checkeq(t, {{"abc"}, 4})
+
+t = m.match((m.C(1)^0/f * m.Cp())/f, "abc")
+checkeq(t, {{"a", "b", "c"}, 4})
+
+t = m.match((m.C(1)^0/f * m.Cp())/f, "xbc")
+checkeq(t, {4})
+
+t = m.match(m.C(m.C(1)^0)/f, "abc")
+checkeq(t, {"abc", "a", "b", "c"})
+
+g = function (...) return 1, ... end
+t = {m.match(m.C(1)^0/g/g, "abc")}
+checkeq(t, {1, 1, "a", "b", "c"})
+
+t = {m.match(m.Cc(nil,nil,4) * m.Cc(nil,3) * m.Cc(nil, nil) / g / g, "")}
+t1 = {1,1,nil,nil,4,nil,3,nil,nil}
+for i=1,10 do assert(t[i] == t1[i]) end
+
+-- bug in 0.12.2: ktable with only nil could be eliminated when joining
+-- with a pattern without ktable
+assert((m.P"aaa" * m.Cc(nil)):match"aaa" == nil)
+
+t = {m.match((m.C(1) / function (x) return x, x.."x" end)^0, "abc")}
+checkeq(t, {"a", "ax", "b", "bx", "c", "cx"})
+
+t = m.match(m.Ct((m.C(1) / function (x,y) return y, x end * m.Cc(1))^0), "abc")
+checkeq(t, {nil, "a", 1, nil, "b", 1, nil, "c", 1})
+
+-- tests for Query Replacements
+
+assert(m.match(m.C(m.C(1)^0)/{abc = 10}, "abc") == 10)
+assert(m.match(m.C(1)^0/{a = 10}, "abc") == 10)
+assert(m.match(m.S("ba")^0/{ab = 40}, "abc") == 40)
+t = m.match(m.Ct((m.S("ba")/{a = 40})^0), "abc")
+checkeq(t, {40})
+
+assert(m.match(m.Cs((m.C(1)/{a=".", d=".."})^0), "abcdde") == ".bc....e")
+assert(m.match(m.Cs((m.C(1)/{f="."})^0), "abcdde") == "abcdde")
+assert(m.match(m.Cs((m.C(1)/{d="."})^0), "abcdde") == "abc..e")
+assert(m.match(m.Cs((m.C(1)/{e="."})^0), "abcdde") == "abcdd.")
+assert(m.match(m.Cs((m.C(1)/{e=".", f="+"})^0), "eefef") == "..+.+")
+assert(m.match(m.Cs((m.C(1))^0), "abcdde") == "abcdde")
+assert(m.match(m.Cs(m.C(m.C(1)^0)), "abcdde") == "abcdde")
+assert(m.match(1 * m.Cs(m.P(1)^0), "abcdde") == "bcdde")
+assert(m.match(m.Cs((m.C('0')/'x' + 1)^0), "abcdde") == "abcdde")
+assert(m.match(m.Cs((m.C('0')/'x' + 1)^0), "0ab0b0") == "xabxbx")
+assert(m.match(m.Cs((m.C('0')/'x' + m.P(1)/{b=3})^0), "b0a0b") == "3xax3")
+assert(m.match(m.P(1)/'%0%0'/{aa = -3} * 'x', 'ax') == -3)
+assert(m.match(m.C(1)/'%0%1'/{aa = 'z'}/{z = -3} * 'x', 'ax') == -3)
+
+assert(m.match(m.Cs(m.Cc(0) * (m.P(1)/"")), "4321") == "0")
+
+assert(m.match(m.Cs((m.P(1) / "%0")^0), "abcd") == "abcd")
+assert(m.match(m.Cs((m.P(1) / "%0.%0")^0), "abcd") == "a.ab.bc.cd.d")
+assert(m.match(m.Cs((m.P("a") / "%0.%0" + 1)^0), "abcad") == "a.abca.ad")
+assert(m.match(m.C("a") / "%1%%%0", "a") == "a%a")
+assert(m.match(m.Cs((m.P(1) / ".xx")^0), "abcd") == ".xx.xx.xx.xx")
+assert(m.match(m.Cp() * m.P(3) * m.Cp()/"%2%1%1 - %0 ", "abcde") ==
+ "411 - abc ")
+
+assert(m.match(m.P(1)/"%0", "abc") == "a")
+checkerr("invalid capture index", m.match, m.P(1)/"%1", "abc")
+checkerr("invalid capture index", m.match, m.P(1)/"%9", "abc")
+
+p = m.C(1)
+p = p * p; p = p * p; p = p * p * m.C(1) / "%9 - %1"
+assert(p:match("1234567890") == "9 - 1")
+
+assert(m.match(m.Cc(print), "") == print)
+
+-- too many captures (just ignore extra ones)
+p = m.C(1)^0 / "%2-%9-%0-%9"
+assert(p:match"01234567890123456789" == "1-8-01234567890123456789-8")
+s = string.rep("12345678901234567890", 20)
+assert(m.match(m.C(1)^0 / "%9-%1-%0-%3", s) == "9-1-" .. s .. "-3")
+
+-- string captures with non-string subcaptures
+p = m.Cc('alo') * m.C(1) / "%1 - %2 - %1"
+assert(p:match'x' == 'alo - x - alo')
+
+checkerr("invalid capture value (a boolean)", m.match, m.Cc(true) / "%1", "a")
+
+-- long strings for string capture
+l = 10000
+s = string.rep('a', l) .. string.rep('b', l) .. string.rep('c', l)
+
+p = (m.C(m.P'a'^1) * m.C(m.P'b'^1) * m.C(m.P'c'^1)) / '%3%2%1'
+
+assert(p:match(s) == string.rep('c', l) ..
+ string.rep('b', l) ..
+ string.rep('a', l))
+
+print"+"
+
+-- accumulator capture
+function f (x) return x + 1 end
+assert(m.match(m.Cf(m.Cc(0) * m.C(1)^0, f), "alo alo") == 7)
+
+t = {m.match(m.Cf(m.Cc(1,2,3), error), "")}
+checkeq(t, {1})
+p = m.Cf(m.Ct(true) * m.Cg(m.C(m.R"az"^1) * "=" * m.C(m.R"az"^1) * ";")^0,
+ rawset)
+t = p:match("a=b;c=du;xux=yuy;")
+checkeq(t, {a="b", c="du", xux="yuy"})
+
+
+-- errors in accumulator capture
+
+-- no initial capture
+checkerr("no initial value", m.match, m.Cf(m.P(5), print), 'aaaaaa')
+-- no initial capture (very long match forces fold to be a pair open-close)
+checkerr("no initial value", m.match, m.Cf(m.P(500), print),
+ string.rep('a', 600))
+
+-- nested capture produces no initial value
+checkerr("no initial value", m.match, m.Cf(m.P(1) / {}, print), "alo")
+
+
+-- tests for loop checker
+
+local function isnullable (p)
+ checkerr("may accept empty string", function (p) return p^0 end, m.P(p))
+end
+
+isnullable(m.P("x")^-4)
+assert(m.match(((m.P(0) + 1) * m.S"al")^0, "alo") == 3)
+assert(m.match((("x" + #m.P(1))^-4 * m.S"al")^0, "alo") == 3)
+isnullable("")
+isnullable(m.P("x")^0)
+isnullable(m.P("x")^-1)
+isnullable(m.P("x") + 1 + 2 + m.P("a")^-1)
+isnullable(-m.P("ab"))
+isnullable(- -m.P("ab"))
+isnullable(# #(m.P("ab") + "xy"))
+isnullable(- #m.P("ab")^0)
+isnullable(# -m.P("ab")^1)
+isnullable(#m.V(3))
+isnullable(m.V(3) + m.V(1) + m.P('a')^-1)
+isnullable({[1] = m.V(2) * m.V(3), [2] = m.V(3), [3] = m.P(0)})
+assert(m.match(m.P{[1] = m.V(2) * m.V(3), [2] = m.V(3), [3] = m.P(1)}^0, "abc")
+ == 3)
+assert(m.match(m.P""^-3, "a") == 1)
+
+local function find (p, s)
+ return m.match(basiclookfor(p), s)
+end
+
+
+local function badgrammar (g, expected)
+ local stat, msg = pcall(m.P, g)
+ assert(not stat)
+ if expected then assert(find(expected, msg)) end
+end
+
+badgrammar({[1] = m.V(1)}, "rule '1'")
+badgrammar({[1] = m.V(2)}, "rule '2'") -- invalid non-terminal
+badgrammar({[1] = m.V"x"}, "rule 'x'") -- invalid non-terminal
+badgrammar({[1] = m.V{}}, "rule '(a table)'") -- invalid non-terminal
+badgrammar({[1] = #m.P("a") * m.V(1)}, "rule '1'") -- left-recursive
+badgrammar({[1] = -m.P("a") * m.V(1)}, "rule '1'") -- left-recursive
+badgrammar({[1] = -1 * m.V(1)}, "rule '1'") -- left-recursive
+badgrammar({[1] = -1 + m.V(1)}, "rule '1'") -- left-recursive
+badgrammar({[1] = 1 * m.V(2), [2] = m.V(2)}, "rule '2'") -- left-recursive
+badgrammar({[1] = 1 * m.V(2)^0, [2] = m.P(0)}, "rule '1'") -- inf. loop
+badgrammar({ m.V(2), m.V(3)^0, m.P"" }, "rule '2'") -- inf. loop
+badgrammar({ m.V(2) * m.V(3)^0, m.V(3)^0, m.P"" }, "rule '1'") -- inf. loop
+badgrammar({"x", x = #(m.V(1) * 'a') }, "rule '1'") -- inf. loop
+badgrammar({ -(m.V(1) * 'a') }, "rule '1'") -- inf. loop
+badgrammar({"x", x = m.P'a'^-1 * m.V"x"}, "rule 'x'") -- left recursive
+badgrammar({"x", x = m.P'a' * m.V"y"^1, y = #m.P(1)}, "rule 'x'")
+
+assert(m.match({'a' * -m.V(1)}, "aaa") == 2)
+assert(m.match({'a' * -m.V(1)}, "aaaa") == nil)
+
+
+-- good x bad grammars
+m.P{ ('a' * m.V(1))^-1 }
+m.P{ -('a' * m.V(1)) }
+m.P{ ('abc' * m.V(1))^-1 }
+m.P{ -('abc' * m.V(1)) }
+badgrammar{ #m.P('abc') * m.V(1) }
+badgrammar{ -('a' + m.V(1)) }
+m.P{ #('a' * m.V(1)) }
+badgrammar{ #('a' + m.V(1)) }
+m.P{ m.B{ m.P'abc' } * 'a' * m.V(1) }
+badgrammar{ m.B{ m.P'abc' } * m.V(1) }
+badgrammar{ ('a' + m.P'bcd')^-1 * m.V(1) }
+
+
+-- simple tests for maximum sizes:
+local p = m.P"a"
+for i=1,14 do p = p * p end
+
+p = {}
+for i=1,100 do p[i] = m.P"a" end
+p = m.P(p)
+
+
+-- strange values for rule labels
+
+p = m.P{ "print",
+ print = m.V(print),
+ [print] = m.V(_G),
+ [_G] = m.P"a",
+ }
+
+assert(p:match("a"))
+
+-- initial rule
+g = {}
+for i = 1, 10 do g["i"..i] = "a" * m.V("i"..i+1) end
+g.i11 = m.P""
+for i = 1, 10 do
+ g[1] = "i"..i
+ local p = m.P(g)
+ assert(p:match("aaaaaaaaaaa") == 11 - i + 1)
+end
+
+print"+"
+
+
+-- tests for back references
+checkerr("back reference 'x' not found", m.match, m.Cb('x'), '')
+checkerr("back reference 'b' not found", m.match, m.Cg(1, 'a') * m.Cb('b'), 'a')
+
+p = m.Cg(m.C(1) * m.C(1), "k") * m.Ct(m.Cb("k"))
+t = p:match("ab")
+checkeq(t, {"a", "b"})
+
+p = m.P(true)
+for i = 1, 10 do p = p * m.Cg(1, i) end
+for i = 1, 10 do
+ local p = p * m.Cb(i)
+ assert(p:match('abcdefghij') == string.sub('abcdefghij', i, i))
+end
+
+
+t = {}
+function foo (p) t[#t + 1] = p; return p .. "x" end
+
+p = m.Cg(m.C(2) / foo, "x") * m.Cb"x" *
+ m.Cg(m.Cb('x') / foo, "x") * m.Cb"x" *
+ m.Cg(m.Cb('x') / foo, "x") * m.Cb"x" *
+ m.Cg(m.Cb('x') / foo, "x") * m.Cb"x"
+x = {p:match'ab'}
+checkeq(x, {'abx', 'abxx', 'abxxx', 'abxxxx'})
+checkeq(t, {'ab',
+ 'ab', 'abx',
+ 'ab', 'abx', 'abxx',
+ 'ab', 'abx', 'abxx', 'abxxx'})
+
+
+
+-- tests for match-time captures
+
+p = m.P'a' * (function (s, i) return (s:sub(i, i) == 'b') and i + 1 end)
+ + 'acd'
+
+assert(p:match('abc') == 3)
+assert(p:match('acd') == 4)
+
+local function id (s, i, ...)
+ return true, ...
+end
+
+assert(m.Cmt(m.Cs((m.Cmt(m.S'abc' / { a = 'x', c = 'y' }, id) +
+ m.R'09'^1 / string.char +
+ m.P(1))^0), id):match"acb98+68c" == "xyb\98+\68y")
+
+p = m.P{'S',
+ S = m.V'atom' * space
+ + m.Cmt(m.Ct("(" * space * (m.Cmt(m.V'S'^1, id) + m.P(true)) * ")" * space), id),
+ atom = m.Cmt(m.C(m.R("AZ", "az", "09")^1), id)
+}
+x = p:match"(a g () ((b) c) (d (e)))"
+checkeq(x, {'a', 'g', {}, {{'b'}, 'c'}, {'d', {'e'}}});
+
+x = {(m.Cmt(1, id)^0):match(string.rep('a', 500))}
+assert(#x == 500)
+
+local function id(s, i, x)
+ if x == 'a' then return i, 1, 3, 7
+ else return nil, 2, 4, 6, 8
+ end
+end
+
+p = ((m.P(id) * 1 + m.Cmt(2, id) * 1 + m.Cmt(1, id) * 1))^0
+assert(table.concat{p:match('abababab')} == string.rep('137', 4))
+
+local function ref (s, i, x)
+ return m.match(x, s, i - x:len())
+end
+
+assert(m.Cmt(m.P(1)^0, ref):match('alo') == 4)
+assert((m.P(1) * m.Cmt(m.P(1)^0, ref)):match('alo') == 4)
+assert(not (m.P(1) * m.Cmt(m.C(1)^0, ref)):match('alo'))
+
+ref = function (s,i,x) return i == tonumber(x) and i, 'xuxu' end
+
+assert(m.Cmt(1, ref):match'2')
+assert(not m.Cmt(1, ref):match'1')
+assert(m.Cmt(m.P(1)^0, ref):match'03')
+
+function ref (s, i, a, b)
+ if a == b then return i, a:upper() end
+end
+
+p = m.Cmt(m.C(m.R"az"^1) * "-" * m.C(m.R"az"^1), ref)
+p = (any - p)^0 * p * any^0 * -1
+
+assert(p:match'abbbc-bc ddaa' == 'BC')
+
+do -- match-time captures cannot be optimized away
+ local touch = 0
+ f = m.P(function () touch = touch + 1; return true end)
+
+ local function check(n) n = n or 1; assert(touch == n); touch = 0 end
+
+ assert(m.match(f * false + 'b', 'a') == nil); check()
+ assert(m.match(f * false + 'b', '') == nil); check()
+ assert(m.match( (f * 'a')^0 * 'b', 'b') == 2); check()
+ assert(m.match( (f * 'a')^0 * 'b', '') == nil); check()
+ assert(m.match( (f * 'a')^-1 * 'b', 'b') == 2); check()
+ assert(m.match( (f * 'a')^-1 * 'b', '') == nil); check()
+ assert(m.match( ('b' + f * 'a')^-1 * 'b', '') == nil); check()
+ assert(m.match( (m.P'b'^-1 * f * 'a')^-1 * 'b', '') == nil); check()
+ assert(m.match( (-m.P(1) * m.P'b'^-1 * f * 'a')^-1 * 'b', '') == nil);
+ check()
+ assert(m.match( (f * 'a' + 'b')^-1 * 'b', '') == nil); check()
+ assert(m.match(f * 'a' + f * 'b', 'b') == 2); check(2)
+ assert(m.match(f * 'a' + f * 'b', 'a') == 2); check(1)
+ assert(m.match(-f * 'a' + 'b', 'b') == 2); check(1)
+ assert(m.match(-f * 'a' + 'b', '') == nil); check(1)
+end
+
+c = '[' * m.Cg(m.P'='^0, "init") * '[' *
+ { m.Cmt(']' * m.C(m.P'='^0) * ']' * m.Cb("init"), function (_, _, s1, s2)
+ return s1 == s2 end)
+ + 1 * m.V(1) } / 0
+
+assert(c:match'[==[]]====]]]]==]===[]' == 18)
+assert(c:match'[[]=]====]=]]]==]===[]' == 14)
+assert(not c:match'[[]=]====]=]=]==]===[]')
+
+
+-- old bug: optimization of concat with fail removed match-time capture
+p = m.Cmt(0, function (s) p = s end) * m.P(false)
+assert(not p:match('alo'))
+assert(p == 'alo')
+
+
+-- ensure that failed match-time captures are not kept on Lua stack
+do
+ local t = {__mode = "kv"}; setmetatable(t,t)
+ local c = 0
+
+ local function foo (s,i)
+ collectgarbage();
+ assert(next(t) == "__mode" and next(t, "__mode") == nil)
+ local x = {}
+ t[x] = true
+ c = c + 1
+ return i, x
+ end
+
+ local p = m.P{ m.Cmt(0, foo) * m.P(false) + m.P(1) * m.V(1) + m.P"" }
+ p:match(string.rep('1', 10))
+ assert(c == 11)
+end
+
+
+-- Return a match-time capture that returns 'n' captures
+local function manyCmt (n)
+ return m.Cmt("a", function ()
+ local a = {}; for i = 1, n do a[i] = n - i end
+ return true, unpack(a)
+ end)
+end
+
+-- bug in 1.0: failed match-time that used previous match-time results
+do
+ local x
+ local function aux (...) x = #{...}; return false end
+ local res = {m.match(m.Cmt(manyCmt(20), aux) + manyCmt(10), "a")}
+ assert(#res == 10 and res[1] == 9 and res[10] == 0)
+end
+
+
+-- bug in 1.0: problems with math-times returning too many captures
+do
+ local lim = 2^11 - 10
+ local res = {m.match(manyCmt(lim), "a")}
+ assert(#res == lim and res[1] == lim - 1 and res[lim] == 0)
+ checkerr("too many", m.match, manyCmt(2^15), "a")
+end
+
+p = (m.P(function () return true, "a" end) * 'a'
+ + m.P(function (s, i) return i, "aa", 20 end) * 'b'
+ + m.P(function (s,i) if i <= #s then return i, "aaa" end end) * 1)^0
+
+t = {p:match('abacc')}
+checkeq(t, {'a', 'aa', 20, 'a', 'aaa', 'aaa'})
+
+
+-------------------------------------------------------------------
+-- Tests for 're' module
+-------------------------------------------------------------------
+
+local re = require "re"
+
+local match, compile = re.match, re.compile
+
+
+
+assert(match("a", ".") == 2)
+assert(match("a", "''") == 1)
+assert(match("", " ! . ") == 1)
+assert(not match("a", " ! . "))
+assert(match("abcde", " ( . . ) * ") == 5)
+assert(match("abbcde", " [a-c] +") == 5)
+assert(match("0abbc1de", "'0' [a-c]+ '1'") == 7)
+assert(match("0zz1dda", "'0' [^a-c]+ 'a'") == 8)
+assert(match("abbc--", " [a-c] + +") == 5)
+assert(match("abbc--", " [ac-] +") == 2)
+assert(match("abbc--", " [-acb] + ") == 7)
+assert(not match("abbcde", " [b-z] + "))
+assert(match("abb\"de", '"abb"["]"de"') == 7)
+assert(match("abceeef", "'ac' ? 'ab' * 'c' { 'e' * } / 'abceeef' ") == "eee")
+assert(match("abceeef", "'ac'? 'ab'* 'c' { 'f'+ } / 'abceeef' ") == 8)
+
+assert(re.match("aaand", "[a]^2") == 3)
+
+local t = {match("abceefe", "( ( & 'e' {} ) ? . ) * ")}
+checkeq(t, {4, 5, 7})
+local t = {match("abceefe", "((&&'e' {})? .)*")}
+checkeq(t, {4, 5, 7})
+local t = {match("abceefe", "( ( ! ! 'e' {} ) ? . ) *")}
+checkeq(t, {4, 5, 7})
+local t = {match("abceefe", "(( & ! & ! 'e' {})? .)*")}
+checkeq(t, {4, 5, 7})
+
+assert(match("cccx" , "'ab'? ('ccc' / ('cde' / 'cd'*)? / 'ccc') 'x'+") == 5)
+assert(match("cdx" , "'ab'? ('ccc' / ('cde' / 'cd'*)? / 'ccc') 'x'+") == 4)
+assert(match("abcdcdx" , "'ab'? ('ccc' / ('cde' / 'cd'*)? / 'ccc') 'x'+") == 8)
+
+assert(match("abc", "a <- (. a)?") == 4)
+b = "balanced <- '(' ([^()] / balanced)* ')'"
+assert(match("(abc)", b))
+assert(match("(a(b)((c) (d)))", b))
+assert(not match("(a(b ((c) (d)))", b))
+
+b = compile[[ balanced <- "(" ([^()] / balanced)* ")" ]]
+assert(b == m.P(b))
+assert(b:match"((((a))(b)))")
+
+local g = [[
+ S <- "0" B / "1" A / "" -- balanced strings
+ A <- "0" S / "1" A A -- one more 0
+ B <- "1" S / "0" B B -- one more 1
+]]
+assert(match("00011011", g) == 9)
+
+local g = [[
+ S <- ("0" B / "1" A)*
+ A <- "0" / "1" A A
+ B <- "1" / "0" B B
+]]
+assert(match("00011011", g) == 9)
+assert(match("000110110", g) == 9)
+assert(match("011110110", g) == 3)
+assert(match("000110010", g) == 1)
+
+s = "aaaaaaaaaaaaaaaaaaaaaaaa"
+assert(match(s, "'a'^3") == 4)
+assert(match(s, "'a'^0") == 1)
+assert(match(s, "'a'^+3") == s:len() + 1)
+assert(not match(s, "'a'^+30"))
+assert(match(s, "'a'^-30") == s:len() + 1)
+assert(match(s, "'a'^-5") == 6)
+for i = 1, s:len() do
+ assert(match(s, string.format("'a'^+%d", i)) >= i + 1)
+ assert(match(s, string.format("'a'^-%d", i)) <= i + 1)
+ assert(match(s, string.format("'a'^%d", i)) == i + 1)
+end
+assert(match("01234567890123456789", "[0-9]^3+") == 19)
+
+
+assert(match("01234567890123456789", "({....}{...}) -> '%2%1'") == "4560123")
+t = match("0123456789", "{| {.}* |}")
+checkeq(t, {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"})
+assert(match("012345", "{| (..) -> '%0%0' |}")[1] == "0101")
+
+assert(match("abcdef", "( {.} {.} {.} {.} {.} ) -> 3") == "c")
+assert(match("abcdef", "( {:x: . :} {.} {.} {.} {.} ) -> 3") == "d")
+assert(match("abcdef", "( {:x: . :} {.} {.} {.} {.} ) -> 0") == 6)
+
+assert(not match("abcdef", "{:x: ({.} {.} {.}) -> 2 :} =x"))
+assert(match("abcbef", "{:x: ({.} {.} {.}) -> 2 :} =x"))
+
+eqcharset(compile"[]]", "]")
+eqcharset(compile"[][]", m.S"[]")
+eqcharset(compile"[]-]", m.S"-]")
+eqcharset(compile"[-]", m.S"-")
+eqcharset(compile"[az-]", m.S"a-z")
+eqcharset(compile"[-az]", m.S"a-z")
+eqcharset(compile"[a-z]", m.R"az")
+eqcharset(compile"[]['\"]", m.S[[]['"]])
+
+eqcharset(compile"[^]]", any - "]")
+eqcharset(compile"[^][]", any - m.S"[]")
+eqcharset(compile"[^]-]", any - m.S"-]")
+eqcharset(compile"[^]-]", any - m.S"-]")
+eqcharset(compile"[^-]", any - m.S"-")
+eqcharset(compile"[^az-]", any - m.S"a-z")
+eqcharset(compile"[^-az]", any - m.S"a-z")
+eqcharset(compile"[^a-z]", any - m.R"az")
+eqcharset(compile"[^]['\"]", any - m.S[[]['"]])
+
+-- tests for comments in 're'
+e = compile[[
+A <- _B -- \t \n %nl .<> <- -> --
+_B <- 'x' --]]
+assert(e:match'xy' == 2)
+
+-- tests for 're' with pre-definitions
+defs = {digits = m.R"09", letters = m.R"az", _=m.P"__"}
+e = compile("%letters (%letters / %digits)*", defs)
+assert(e:match"x123" == 5)
+e = compile("%_", defs)
+assert(e:match"__" == 3)
+
+e = compile([[
+ S <- A+
+ A <- %letters+ B
+ B <- %digits+
+]], defs)
+
+e = compile("{[0-9]+'.'?[0-9]*} -> sin", math)
+assert(e:match("2.34") == math.sin(2.34))
+
+
+function eq (_, _, a, b) return a == b end
+
+c = re.compile([[
+ longstring <- '[' {:init: '='* :} '[' close
+ close <- ']' =init ']' / . close
+]])
+
+assert(c:match'[==[]]===]]]]==]===[]' == 17)
+assert(c:match'[[]=]====]=]]]==]===[]' == 14)
+assert(not c:match'[[]=]====]=]=]==]===[]')
+
+c = re.compile" '[' {:init: '='* :} '[' (!(']' =init ']') .)* ']' =init ']' !. "
+
+assert(c:match'[==[]]===]]]]==]')
+assert(c:match'[[]=]====]=][]==]===[]]')
+assert(not c:match'[[]=]====]=]=]==]===[]')
+
+assert(re.find("hi alalo", "{:x:..:} =x") == 4)
+assert(re.find("hi alalo", "{:x:..:} =x", 4) == 4)
+assert(not re.find("hi alalo", "{:x:..:} =x", 5))
+assert(re.find("hi alalo", "{'al'}", 5) == 6)
+assert(re.find("hi aloalolo", "{:x:..:} =x") == 8)
+assert(re.find("alo alohi x x", "{:word:%w+:}%W*(=word)!%w") == 11)
+
+-- re.find discards any captures
+local a,b,c = re.find("alo", "{.}{'o'}")
+assert(a == 2 and b == 3 and c == nil)
+
+local function match (s,p)
+ local i,e = re.find(s,p)
+ if i then return s:sub(i, e) end
+end
+assert(match("alo alo", '[a-z]+') == "alo")
+assert(match("alo alo", '{:x: [a-z]+ :} =x') == nil)
+assert(match("alo alo", "{:x: [a-z]+ :} ' ' =x") == "alo alo")
+
+assert(re.gsub("alo alo", "[abc]", "x") == "xlo xlo")
+assert(re.gsub("alo alo", "%w+", ".") == ". .")
+assert(re.gsub("hi, how are you", "[aeiou]", string.upper) ==
+ "hI, hOw ArE yOU")
+
+s = 'hi [[a comment[=]=] ending here]] and [=[another]]=]]'
+c = re.compile" '[' {:i: '='* :} '[' (!(']' =i ']') .)* ']' { =i } ']' "
+assert(re.gsub(s, c, "%2") == 'hi and =]')
+assert(re.gsub(s, c, "%0") == s)
+assert(re.gsub('[=[hi]=]', c, "%2") == '=')
+
+assert(re.find("", "!.") == 1)
+assert(re.find("alo", "!.") == 4)
+
+function addtag (s, i, t, tag) t.tag = tag; return i, t end
+
+c = re.compile([[
+ doc <- block !.
+ block <- (start {| (block / { [^<]+ })* |} end?) => addtag
+ start <- '<' {:tag: [a-z]+ :} '>'
+ end <- '' { =tag } '>'
+]], {addtag = addtag})
+
+x = c:match[[
+hihellobuttotheend]]
+checkeq(x, {tag='x', 'hi', {tag = 'b', 'hello'}, 'but',
+ {'totheend'}})
+
+
+-- test for folding captures
+c = re.compile([[
+ S <- (number (%s+ number)*) ~> add
+ number <- %d+ -> tonumber
+]], {tonumber = tonumber, add = function (a,b) return a + b end})
+assert(c:match("3 401 50") == 3 + 401 + 50)
+
+-- tests for look-ahead captures
+x = {re.match("alo", "&(&{.}) !{'b'} {&(...)} &{..} {...} {!.}")}
+checkeq(x, {"", "alo", ""})
+
+assert(re.match("aloalo",
+ "{~ (((&'al' {.}) -> 'A%1' / (&%l {.}) -> '%1%1') / .)* ~}")
+ == "AallooAalloo")
+
+-- bug in 0.9 (and older versions), due to captures in look-aheads
+x = re.compile[[ {~ (&(. ([a-z]* -> '*')) ([a-z]+ -> '+') ' '*)* ~} ]]
+assert(x:match"alo alo" == "+ +")
+
+-- valid capture in look-ahead (used inside the look-ahead itself)
+x = re.compile[[
+ S <- &({:two: .. :} . =two) {[a-z]+} / . S
+]]
+assert(x:match("hello aloaLo aloalo xuxu") == "aloalo")
+
+
+p = re.compile[[
+ block <- {| {:ident:space*:} line
+ ((=ident !space line) / &(=ident space) block)* |}
+ line <- {[^%nl]*} %nl
+ space <- '_' -- should be ' ', but '_' is simpler for editors
+]]
+
+t= p:match[[
+1
+__1.1
+__1.2
+____1.2.1
+____
+2
+__2.1
+]]
+checkeq(t, {"1", {"1.1", "1.2", {"1.2.1", "", ident = "____"}, ident = "__"},
+ "2", {"2.1", ident = "__"}, ident = ""})
+
+
+-- nested grammars
+p = re.compile[[
+ s <- a b !.
+ b <- ( x <- ('b' x)? )
+ a <- ( x <- 'a' x? )
+]]
+
+assert(p:match'aaabbb')
+assert(p:match'aaa')
+assert(not p:match'bbb')
+assert(not p:match'aaabbba')
+
+-- testing groups
+t = {re.match("abc", "{:S <- {:.:} {S} / '':}")}
+checkeq(t, {"a", "bc", "b", "c", "c", ""})
+
+t = re.match("1234", "{| {:a:.:} {:b:.:} {:c:.{.}:} |}")
+checkeq(t, {a="1", b="2", c="4"})
+t = re.match("1234", "{|{:a:.:} {:b:{.}{.}:} {:c:{.}:}|}")
+checkeq(t, {a="1", b="2", c="4"})
+t = re.match("12345", "{| {:.:} {:b:{.}{.}:} {:{.}{.}:} |}")
+checkeq(t, {"1", b="2", "4", "5"})
+t = re.match("12345", "{| {:.:} {:{:b:{.}{.}:}:} {:{.}{.}:} |}")
+checkeq(t, {"1", "23", "4", "5"})
+t = re.match("12345", "{| {:.:} {{:b:{.}{.}:}} {:{.}{.}:} |}")
+checkeq(t, {"1", "23", "4", "5"})
+
+
+-- testing pre-defined names
+assert(os.setlocale("C") == "C")
+
+function eqlpeggsub (p1, p2)
+ local s1 = cs2str(re.compile(p1))
+ local s2 = string.gsub(allchar, "[^" .. p2 .. "]", "")
+ -- if s1 ~= s2 then print(#s1,#s2) end
+ assert(s1 == s2)
+end
+
+
+eqlpeggsub("%w", "%w")
+eqlpeggsub("%a", "%a")
+eqlpeggsub("%l", "%l")
+eqlpeggsub("%u", "%u")
+eqlpeggsub("%p", "%p")
+eqlpeggsub("%d", "%d")
+eqlpeggsub("%x", "%x")
+eqlpeggsub("%s", "%s")
+eqlpeggsub("%c", "%c")
+
+eqlpeggsub("%W", "%W")
+eqlpeggsub("%A", "%A")
+eqlpeggsub("%L", "%L")
+eqlpeggsub("%U", "%U")
+eqlpeggsub("%P", "%P")
+eqlpeggsub("%D", "%D")
+eqlpeggsub("%X", "%X")
+eqlpeggsub("%S", "%S")
+eqlpeggsub("%C", "%C")
+
+eqlpeggsub("[%w]", "%w")
+eqlpeggsub("[_%w]", "_%w")
+eqlpeggsub("[^%w]", "%W")
+eqlpeggsub("[%W%S]", "%W%S")
+
+re.updatelocale()
+
+
+-- testing nested substitutions x string captures
+
+p = re.compile[[
+ text <- {~ item* ~}
+ item <- macro / [^()] / '(' item* ')'
+ arg <- ' '* {~ (!',' item)* ~}
+ args <- '(' arg (',' arg)* ')'
+ macro <- ('apply' args) -> '%1(%2)'
+ / ('add' args) -> '%1 + %2'
+ / ('mul' args) -> '%1 * %2'
+]]
+
+assert(p:match"add(mul(a,b), apply(f,x))" == "a * b + f(x)")
+
+rev = re.compile[[ R <- (!.) -> '' / ({.} R) -> '%2%1']]
+
+assert(rev:match"0123456789" == "9876543210")
+
+
+-- testing error messages in re
+
+local function errmsg (p, err)
+ checkerr(err, re.compile, p)
+end
+
+errmsg('aaaa', "rule 'aaaa'")
+errmsg('a', 'outside')
+errmsg('b <- a', 'undefined')
+errmsg("x <- 'a' x <- 'b'", 'already defined')
+errmsg("'a' -", "near '-'")
+
+
+print"OK"
+
+
From c4b8d320dd7e55ac5a2a24a1ed7337993f870637 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Thu, 25 Apr 2019 06:28:28 +0800
Subject: [PATCH 007/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0lpeg,=20=E5=BE=AE?=
=?UTF-8?q?=E8=B0=83makefile?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
luaclib/src/lpeg/makefile | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile
index 8a5d4061..028b30d2 100644
--- a/luaclib/src/lpeg/makefile
+++ b/luaclib/src/lpeg/makefile
@@ -1,3 +1,11 @@
+.PHONY : build rebuild clean
+
+default :
+ @echo "======================================="
+ @echo "Please use 'make build' command to build it.."
+ @echo "Please use 'make clean' command to clean all."
+ @echo "======================================="
+
LIBNAME = lpeg
LUADIR = ../lua/
@@ -38,13 +46,13 @@ macosx:
build: $(FILES)
CC -o lpeg.so $(FILES) -shared -fPIC -lcore
mv *.so ../../
- rm *.o *.so
+ rm -rf *.o *.so
rebuild: $(FILES)
CC -o lpeg.so $(FILES) -shared -fPIC -lcore
mv *.so ../../
- rm *.o *.so
+ rm -rf *.o *.so
lpeg.so: $(FILES)
From b79943cc9aaa8f4b03eba8d35fb3686cf8f0678f Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Thu, 25 Apr 2019 08:48:47 +0800
Subject: [PATCH 008/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DCC=E7=9A=84=E4=B8=80?=
=?UTF-8?q?=E4=B8=AA=E5=91=BD=E4=BB=A4=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98?=
=?UTF-8?q?.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
luaclib/src/lpeg/makefile | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile
index 028b30d2..adc2b3b6 100644
--- a/luaclib/src/lpeg/makefile
+++ b/luaclib/src/lpeg/makefile
@@ -44,13 +44,13 @@ macosx:
$(MAKE) lpeg.so "DLLFLAGS = -bundle -undefined dynamic_lookup"
build: $(FILES)
- CC -o lpeg.so $(FILES) -shared -fPIC -lcore
+ $(CC) -o lpeg.so $(FILES) -shared -fPIC -lcore
mv *.so ../../
rm -rf *.o *.so
rebuild: $(FILES)
- CC -o lpeg.so $(FILES) -shared -fPIC -lcore
+ $(CC) -o lpeg.so $(FILES) -shared -fPIC -lcore
mv *.so ../../
rm -rf *.o *.so
From 57c17c2b7640ab3466baa8b5d983cecdac352c3b Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 26 Apr 2019 20:01:47 +0800
Subject: [PATCH 009/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Nginx/Traefik?=
=?UTF-8?q?=E5=81=9A=E4=B8=BA=E8=B4=9F=E8=BD=BD=E5=9D=87=E8=A1=A1=E7=9A=84?=
=?UTF-8?q?=E7=A4=BA=E4=BE=8B=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docker/docker-compose-with-nginx.yaml | 37 +++++++++++++++++++++++++
docker/docker-compose-with-traefik.yaml | 30 ++++++++++++++++++++
docker/docker-compose.yaml | 22 ---------------
docker/nginx.conf | 11 ++++++--
docker/script/main.lua | 16 +++++++++++
5 files changed, 92 insertions(+), 24 deletions(-)
create mode 100644 docker/docker-compose-with-nginx.yaml
create mode 100644 docker/docker-compose-with-traefik.yaml
delete mode 100644 docker/docker-compose.yaml
create mode 100644 docker/script/main.lua
diff --git a/docker/docker-compose-with-nginx.yaml b/docker/docker-compose-with-nginx.yaml
new file mode 100644
index 00000000..5e3d5798
--- /dev/null
+++ b/docker/docker-compose-with-nginx.yaml
@@ -0,0 +1,37 @@
+# docker-compose.yaml
+version: "2"
+services:
+ WebProxy:
+ image: nginx:latest
+ ports:
+ - 80:80
+ volumes:
+ - ./nginx.conf:/etc/nginx/nginx.conf
+ networks:
+ - local
+ links:
+ - WebApp1:webapp1
+ - WebApp2:webapp2
+ - WebApp3:webapp3
+ WebApp1:
+ image: candymi/cfweb:latest
+ volumes:
+ - ./script:/app/script
+ networks:
+ - local
+ WebApp2:
+ image: candymi/cfweb:latest
+ volumes:
+ - ./script:/app/script
+ networks:
+ - local
+ WebApp3:
+ image: candymi/cfweb:latest
+ volumes:
+ - ./script:/app/script
+ networks:
+ - local
+
+networks:
+ local:
+ driver: bridge
diff --git a/docker/docker-compose-with-traefik.yaml b/docker/docker-compose-with-traefik.yaml
new file mode 100644
index 00000000..81084bad
--- /dev/null
+++ b/docker/docker-compose-with-traefik.yaml
@@ -0,0 +1,30 @@
+# docker-compose.yaml
+version: "2"
+services:
+ WebProxy:
+ image: traefik
+ command: --api --docker # Enables the web UI and tells Traefik to listen to docker
+ ports:
+ - "80:80" # The HTTP port
+ - "8080:8080" # The Web UI (enabled by --api)
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
+ networks:
+ - local
+
+ WebApp:
+ image: candymi/cfweb:latest
+ labels:
+ - "traefik.port=8080"
+ - "traefik.backend=WebApp"
+ - "traefik.enable=true"
+ - "traefik.domain=localhost"
+ - "traefik.frontend.rule=Host:localhost"
+ volumes:
+ - ./script:/app/script
+ networks:
+ - local
+
+networks:
+ local:
+ driver: bridge
diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml
deleted file mode 100644
index a45d3817..00000000
--- a/docker/docker-compose.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-# docker-compose.yaml
-version: "2"
-services:
- WebProxy:
- container_name: WebProxy
- image: nginx:latest
- ports:
- - 80:80
- volumes:
- - ./nginx.conf:/etc/nginx/nginx.conf
- networks:
- - local
- links:
- - WebApp:webapp
- WebApp:
- container_name: WebApp
- image: candymi/cfweb:latest
- networks:
- - local
-networks:
- local:
- driver: bridge
diff --git a/docker/nginx.conf b/docker/nginx.conf
index 220eb955..469b57cb 100644
--- a/docker/nginx.conf
+++ b/docker/nginx.conf
@@ -34,19 +34,26 @@ http {
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_disable "MSIE [1-6]\.";
+
+ upstream myweb {
+ server webapp1:8080;
+ server webapp2:8080;
+ server webapp3:8080;
+ }
+
server {
listen 80;
#access_log /var/log/nginx/8080.log main;
location /ws {
- proxy_pass http://webapp:8080/ws;
+ proxy_pass http://myweb/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
- proxy_pass http://webapp:8080;
+ proxy_pass http://myweb;
proxy_http_version 1.1;
proxy_ignore_client_abort on;
proxy_set_header Host $http_host;
diff --git a/docker/script/main.lua b/docker/script/main.lua
new file mode 100644
index 00000000..146d2a66
--- /dev/null
+++ b/docker/script/main.lua
@@ -0,0 +1,16 @@
+local httpd = require "httpd"
+local http = require "httpd.http"
+
+local app = httpd:new("httpd")
+
+app:before(function (content)
+ return http.ok()
+end)
+
+app:api('/api/login', function (content)
+ return '{"code":200, "data":{"token":"admin","uid":1}}'
+end)
+
+app:listen("0.0.0.0", 8080)
+
+app:run()
From ac29e8c39a8d6e87b2bd773bcf006f649912cd67 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Sat, 27 Apr 2019 01:16:21 +0800
Subject: [PATCH 010/956] =?UTF-8?q?=E8=B0=83=E6=95=B4class=E5=88=A4?=
=?UTF-8?q?=E6=96=AD=E3=80=81=E5=88=A0=E9=99=A4Route=E7=9A=84=E4=B8=80?=
=?UTF-8?q?=E4=BA=9B=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/class/init.lua | 15 ++++++++-------
lualib/httpd/Router.lua | 13 +------------
2 files changed, 9 insertions(+), 19 deletions(-)
diff --git a/lualib/class/init.lua b/lualib/class/init.lua
index f762fe41..8b882bae 100644
--- a/lualib/class/init.lua
+++ b/lualib/class/init.lua
@@ -1,11 +1,11 @@
--- a minimal class implementation
+local type = type
+local setmetatable = setmetatable
-- 一个精简版的类实现
function class(cls_name)
- local cls = { }
- cls.__name = cls_name
+ local cls = { __name = cls_name }
cls.__index = cls
cls.__call = function (cls, ...)
- local call = cls[cls.__name]
+ local call = cls[cls_name]
if call then
return call(cls, ...)
end
@@ -16,14 +16,15 @@ function class(cls_name)
return print("Please use ':' to create new object :)")
end
local t = {}
- if not c.ctor then
+ local ctor = c.ctor
+ if type(ctor) ~= 'function' then
print("Can't find ctor to init.")
else
- c.ctor(t, ...)
+ ctor(t, ...)
end
return setmetatable(t, cls)
end
return cls
end
-return class
\ No newline at end of file
+return class
diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua
index 39998aa5..b4813142 100644
--- a/lualib/httpd/Router.lua
+++ b/lualib/httpd/Router.lua
@@ -59,17 +59,6 @@ local function find_route(path)
if #tab == 0 then -- 如果路由为/[/]{0, n}, 则转义为: ''
tab[1] = ''
end
- -- for index, r in ipairs(tab) do
- -- local route = routes[index][r]
- -- if type(route) == 'table' then
- -- if #tab == index then
- -- return route.class, route.type
- -- end
- -- if route.type == Router.STATIC then
- -- return route.class, route.type
- -- end
- -- end
- -- end
for index, route in ipairs(routes) do
local r = tab[index]
if not r then
@@ -106,4 +95,4 @@ function Router.registery(route, class, route_type)
return registery_router(route, class, route_type)
end
-return Router
\ No newline at end of file
+return Router
From c05569c144d3ff3bcab176ca2cf1bd802eee04b9 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Sat, 27 Apr 2019 12:12:37 +0800
Subject: [PATCH 011/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0logging=E6=A8=A1?=
=?UTF-8?q?=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/logging/init.lua | 151 ++++++++++++++++++++++++++++++++++++++++
script/test_logging.lua | 10 +++
2 files changed, 161 insertions(+)
create mode 100644 lualib/logging/init.lua
create mode 100644 script/test_logging.lua
diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua
new file mode 100644
index 00000000..021c421a
--- /dev/null
+++ b/lualib/logging/init.lua
@@ -0,0 +1,151 @@
+-- logging 核心配置
+
+local class = require "class"
+local system = require "system"
+local now = system.now
+local type = type
+local print = print
+local assert = assert
+local pairs = pairs
+local tostring = tostring
+local getmetatable = getmetatable
+
+local modf = math.modf
+local debug_getinfo = debug.getinfo
+local os_date = os.date
+local io_open = io.open
+local format = string.format
+local concat = table.concat
+local modf = math.modf
+
+
+-- 格式化时间: [年-月-日 时:分:秒,毫秒]
+local function fmt_Y_m_d_H_M_S()
+ local ts, f = modf(now())
+ f = format("%3.0f", f * 1e3)
+ return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', f, ']'})
+end
+
+-- 格式化时间: [年-月-日 时:分:秒]
+local function Y_m_d()
+ return os_date('%Y-%m-%d')
+end
+
+-- LOG函数的调用信息
+local function debuginfo ()
+ local info = debug_getinfo(3, 'Sln')
+ return concat({'[', info.source, ':', info.currentline, ']'})
+end
+
+-- 格式化
+local function table_format(t)
+ local tab = {}
+ while 1 do
+ local mt = getmetatable(t)
+ for key, value in pairs(t) do
+ if type(key) == 'number' then
+ if type(value) == 'table' then
+ if t ~= value then
+ tab[#tab+1] = concat({'[', key, ']', '=', '{', concat(t, ", "), '}'})
+ end
+ elseif type(value) == 'string' then
+ tab[#tab+1] = concat({'[', key, ']', '="', tostring(value), '"'})
+ else
+ tab[#tab+1] = concat({'[', key, ']', '=', tostring(value)})
+ end
+ else
+ if type(value) == 'table' then
+ if t ~= value then
+ tab[#tab+1] = concat({'["', key, '"]', '=', '{', concat(t, ", "), '}'})
+ end
+ elseif type(value) == 'string' then
+ tab[#tab+1] = concat({'["', key, '"]', '="', tostring(value), '"'})
+ else
+ tab[#tab+1] = concat({'["', key, '"]', '=', tostring(value)})
+ end
+ end
+ end
+ if not mt then
+ break
+ end
+ t = mt
+ end
+ return concat({'{', concat(tab, ', '), '}'})
+end
+
+local function fmt(...)
+ local args = {...}
+ local index, len = 1, select('#', ...)
+ local tab = {}
+ while 1 do
+ local arg = args[index]
+ if type(arg) == 'table' then
+ tab[#tab+1] = table_format(arg)
+ else
+ tab[#tab+1]= tostring(arg)
+ end
+ if index == len then
+ break
+ end
+ index = index + 1
+ end
+ return concat(tab, ', ')
+end
+
+-- 格式化日志
+function FMT (where, level, ...)
+ return concat({ fmt_Y_m_d_H_M_S(), where, level, ':', fmt(...), '\n'}, ' ')
+end
+
+local paths = {}
+
+local Log = class("Log")
+
+function Log:ctor (opt)
+ self.path = opt.path
+ self.now = Y_m_d()
+end
+
+-- 常规日志
+function Log:INFO (...)
+ print(FMT("\27[32m"..debuginfo(), "[INFO]".."\27[0m", ...))
+ self:dump(FMT(debuginfo(), "[INFO]", ...))
+end
+
+-- 错误日志
+function Log:ERROR (...)
+ print(FMT("\27[31m"..debuginfo(), "[ERROR]".."\27[0m", ...))
+ self:dump(FMT(debuginfo(), "[ERROR]", ...))
+end
+
+-- 调试日志
+function Log:DEBUG (...)
+ print(FMT("\27[36m"..debuginfo(), "[DEBUG]".."\27[0m", ...))
+ self:dump(FMT(debuginfo(), "[DEBUG]", ...))
+end
+
+-- 警告日志
+function Log:WARN (...)
+ print(FMT("\27[33m"..debuginfo(), "[WARN]".."\27[0m", ...))
+ self:dump(FMT(debuginfo(), "[WARN]", ...))
+end
+
+function Log:dump(log)
+ local file = paths[self.path]
+ if type(self.path) == 'string' and self.path ~= '' then
+ if not file then
+ file = io_open(self.path..'_'..self.now..'.log', 'a')
+ paths[self.path] = file
+ else
+ if Y_m_d() ~= self.now then
+ file:close()
+ self.now = Y_m_d()
+ file = io_open(self.path..'_'..self.now..'.log', 'a')
+ paths[self.path] = file
+ end
+ end
+ file:write(log)
+ end
+end
+
+return Log
diff --git a/script/test_logging.lua b/script/test_logging.lua
new file mode 100644
index 00000000..22c22eb4
--- /dev/null
+++ b/script/test_logging.lua
@@ -0,0 +1,10 @@
+local LOG = require "logging"
+
+-- 初始化日志
+local log = LOG:new { path = './admin' }
+
+-- 打印
+log:INFO('this is INFO LOG', nil, 1, nil)
+log:DEBUG('this is DEBUG LOG', nil, nil, 1)
+log:WARN('this is WARN LOG', 1, nil, nil)
+log:ERROR('this is ERROR LOG', nil, nil, nil, log)
From a5fcea3935246195f67ef2647a8e369475c63021 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Sat, 27 Apr 2019 20:56:19 +0800
Subject: [PATCH 012/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=BE=9D=E8=B5=96log?=
=?UTF-8?q?,=20=E4=BD=BF=E7=94=A8cf=E8=87=AA=E5=B7=B1=E5=AE=9E=E7=8E=B0?=
=?UTF-8?q?=E7=9A=84logging=E8=AE=B0=E5=BD=95=E6=97=A5=E5=BF=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/Cache/init.lua | 8 +-
lualib/DB/init.lua | 13 +--
lualib/MQ/init.lua | 8 +-
lualib/httpd/Router.lua | 10 +-
lualib/httpd/init.lua | 18 +---
lualib/internal/Co.lua | 8 +-
lualib/internal/TCP.lua | 17 ++--
lualib/internal/Timer.lua | 11 ++-
lualib/internal/UDP.lua | 3 +-
lualib/log/init.lua | 142 ---------------------------
lualib/logging/init.lua | 8 +-
lualib/protocol/dns.lua | 4 +-
lualib/protocol/http.lua | 16 +--
lualib/protocol/mqtt/init.lua | 10 +-
lualib/protocol/mysql.lua | 1 -
lualib/protocol/redis.lua | 10 +-
lualib/protocol/websocket/server.lua | 18 ++--
script/main.lua | 2 +-
18 files changed, 87 insertions(+), 220 deletions(-)
delete mode 100644 lualib/log/init.lua
diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua
index a1194f51..337ad994 100644
--- a/lualib/Cache/init.lua
+++ b/lualib/Cache/init.lua
@@ -1,4 +1,4 @@
-local log = require "log"
+local log = require "logging"
local Co = require "internal.Co"
local timer = require "internal.Timer"
local redis = require "protocol.redis"
@@ -18,6 +18,8 @@ local upper = string.upper
local lower = string.lower
local splite = string.gmatch
+local Log = log:new()
+
-- 默认情况下, 保持50个redis连接
local MAX, COUNT = 50, 0
@@ -155,7 +157,7 @@ function Cache.init(opt)
if ok then
break
end
- log.error('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接")
+ Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接")
times = times + 1
rds:close()
timer.sleep(3)
@@ -177,4 +179,4 @@ function Cache.count()
return #POOL
end
-return Cache
\ No newline at end of file
+return Cache
diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua
index e5a5184f..74680e16 100644
--- a/lualib/DB/init.lua
+++ b/lualib/DB/init.lua
@@ -1,7 +1,8 @@
local mysql = require "protocol.mysql"
local timer = require "internal.Timer"
local co = require "internal.Co"
-local log = require "log"
+local log = require "logging"
+local Log = log:new()
local co_self = co.self
local co_wait = co.wait
@@ -170,7 +171,7 @@ local function execute(query)
if db.state then
break
end
- log.error(err)
+ Log:ERROR(err)
db:close()
db, ret, err = nil
end
@@ -380,7 +381,7 @@ function DB.init(opt)
if connect then
break
end
- log.error('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接")
+ Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接")
db:close()
times = times + 1
timer.sleep(3)
@@ -414,7 +415,7 @@ function DB.select(fields)
limit = limit,
execute = execute,
}
- if tpy == "string" then
+ if tpy == "string" then
insert(query, fields)
end
if tpy == "table" then
@@ -491,7 +492,7 @@ function DB.query(query)
if db.state then
break
end
- log.error(err)
+ Log:ERROR(err)
db:close()
db, ret, err = nil
end
@@ -508,4 +509,4 @@ function DB.count( ... )
return #POOL
end
-return DB
\ No newline at end of file
+return DB
diff --git a/lualib/MQ/init.lua b/lualib/MQ/init.lua
index 29b02942..9e426776 100644
--- a/lualib/MQ/init.lua
+++ b/lualib/MQ/init.lua
@@ -1,9 +1,11 @@
-local log = require "log"
+local log = require "logging"
local class = require "class"
local Timer = require "internal.Timer"
local mqtt = require "protocol.mqtt"
local redis = require "protocol.redis"
+local Log = log:new()
+
local type = type
local math = math
local random = math.random
@@ -34,7 +36,7 @@ local function mq_login(self)
return rds
end
rds:close()
- log.error('连接mq(redis)失败:'..(err or "unknow")..'.正在尝试重连')
+ Log:ERROR('连接mq(redis)失败:'..(err or "unknow")..'.正在尝试重连')
Timer.sleep(3)
times = times + 1
elseif self.type == 'mqtt' then
@@ -52,7 +54,7 @@ local function mq_login(self)
return mqtt
end
mqtt:close()
- log.error('连接mq(mqtt)失败:'..(err or "unknow")..'.正在尝试重连')
+ Log:ERROR('连接mq(mqtt)失败:'..(err or "unknow")..'.正在尝试重连')
Timer.sleep(3)
times = times + 1
else
diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua
index b4813142..1de99f2f 100644
--- a/lualib/httpd/Router.lua
+++ b/lualib/httpd/Router.lua
@@ -1,4 +1,6 @@
-local log = require "log"
+local log = require "logging"
+local Log = log:new()
+
local math = math
local string = string
local find = string.find
@@ -84,13 +86,13 @@ end
-- 注册路由
function Router.registery(route, class, route_type)
if type(route) ~= 'string' or route == '' then -- 过滤错误的路由输入
- return log.warn('Please Do not add empty string in route registery method :)')
+ return Log:WARN('Please Do not add empty string in route registery method :)')
end
if find(route, '//') then -- 不允许出现路由出现[//]
- return log.warn('Please Do not add [//] in route registery method :)')
+ return Log:WARN('Please Do not add [//] in route registery method :)')
end
if find(route, '^/%[%w+:.+%]$') then -- 不允许顶层路由注册rest模式.
- return log.warn('Please Do not add [/[type:key] in root route :)]')
+ return Log:WARN('Please Do not add [/[type:key] in root route :)]')
end
return registery_router(route, class, route_type)
end
diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua
index a69abdf6..58633bc1 100644
--- a/lualib/httpd/init.lua
+++ b/lualib/httpd/init.lua
@@ -2,7 +2,7 @@ local HTTP = require "protocol.http"
local tcp = require "internal.TCP"
local class = require "class"
local sys = require "system"
-local log = require "log"
+local log = require "logging"
local cf = require "cf"
local type = type
@@ -127,23 +127,13 @@ end
-- 记录日志到文件
function httpd:log(path)
self.logpath = path or "cf-httpd.log"
- log.outfile = self.logpath
+ self.log = log:new({ path = self.logpath })
end
function httpd:tolog(code, path, ip, ip_list, method, speed)
if self.logpath then
- if not self.logfile then
- local err
- self.logfile, err = io_open(self.logpath, "a")
- if not self.logfile then
- return log.error(self.logpath..":"..err)
- end
- end
- local ok, err = self.logfile:write(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed))
- if not ok then
- return log.error(self.logpath..":"..err)
- end
- self.logfile:flush()
+ local log = fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)
+ self.log:dump(log)
end
print(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed))
end
diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua
index e6164b34..191069f4 100644
--- a/lualib/internal/Co.lua
+++ b/lualib/internal/Co.lua
@@ -1,6 +1,8 @@
-local log = require "log"
+local log = require "logging"
local task = require "task"
+local Log = log:new()
+
local type = type
local assert = assert
local error = error
@@ -55,7 +57,7 @@ local function f()
while 1 do
local ok, msg = pcall(co_wait())
if not ok then
- log.error(msg)
+ Log:ERROR(msg)
end
local co, main = co_self()
if not main then
@@ -100,7 +102,7 @@ function Co.wakeup(co, ...)
assert(type(co) == 'thread', "试图传递一个非协程的类型的参数到wakeup内部.")
local status = co_status(co)
if co == co_self() then
- return log.error("不能唤醒当前正在执行的协程")
+ return Log:ERROR("不能唤醒当前正在执行的协程")
end
if main_co == co and status == "suspended" then
return task_start(main_task, main_co, ...)
diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua
index 4a2de9f5..d1e0b284 100644
--- a/lualib/internal/TCP.lua
+++ b/lualib/internal/TCP.lua
@@ -3,7 +3,8 @@ local dns = require "protocol.dns"
local co = require "internal.Co"
local class = require "class"
local tcp = require "tcp"
-local log = require "log"
+local log = require "logging"
+local Log = log:new()
local split = string.sub
local insert = table.insert
@@ -79,7 +80,7 @@ end
function TCP:send(buf)
if self.ssl then
- return log.error("Please use ssl_send method :)")
+ return Log:ERROR("Please use ssl_send method :)")
end
while 1 do
local len = tcp_write(self.fd, buf, #buf)
@@ -114,7 +115,7 @@ end
function TCP:ssl_send(buf)
if not self.ssl then
- return log.error("Please use send method :)")
+ return Log:ERROR("Please use send method :)")
end
while 1 do
local len = tcp_ssl_write(self.ssl, buf, #buf)
@@ -151,7 +152,7 @@ end
function TCP:recv(bytes)
if self.ssl then
- return log.error("Please use ssl_recv method :)")
+ return Log:ERROR("Please use ssl_recv method :)")
end
self.READ_IO = tcp_pop()
local co = co_self()
@@ -187,7 +188,7 @@ end
function TCP:ssl_recv(bytes)
if not self.ssl then
- return log.error("Please use recv method :)")
+ return Log:ERROR("Please use recv method :)")
end
local buf, len = tcp_sslread(self.ssl, bytes)
if not buf then
@@ -243,7 +244,7 @@ function TCP:listen(ip, port, cb)
self.LISTEN_IO = tcp_pop()
self.fd = tcp_new_server_fd(ip, port)
if not self.fd then
- return log.error("this IP and port Create A bind or listen method Faild! :) ")
+ return Log:ERROR("this IP and port Create A bind or listen method Faild! :) ")
end
self.co = co_new(function (fd, ipaddr)
while 1 do
@@ -263,7 +264,7 @@ function TCP:connect(domain, port)
end
self.fd = tcp_new_client_fd(IP, port)
if not self.fd then
- log.error("Connect This IP or Port Faild!"..domain, IP)
+ Log:ERROR("Connect This IP or Port Faild!"..domain, IP)
return nil, "Connect This host fault! :"
end
local co = co_self()
@@ -304,7 +305,7 @@ function TCP:ssl_connect(domain, port)
end
self.ssl_ctx, self.ssl = tcp.new_ssl(self.fd)
if not self.ssl_ctx or not self.ssl then
- return log.error("Create a SSL Error! :) ")
+ return Log:ERROR("Create a SSL Error! :) ")
end
local co = co_self()
self.CONNECT_IO = tcp_pop()
diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua
index 63f02529..9966aeb3 100644
--- a/lualib/internal/Timer.lua
+++ b/lualib/internal/Timer.lua
@@ -1,6 +1,7 @@
local co = require "internal.Co"
local ti = require "timer"
-local log = require "log"
+local log = require "logging"
+local Log = log:new()
local type = type
local pcall = pcall
@@ -48,7 +49,7 @@ function Timer.timeout(timeout, cb)
end
local t = Timer_new()
if not t then
- return log.error("timeout error: Create timer class error! memory maybe not enough...")
+ return Log:ERROR("timeout error: Create timer class error! memory maybe not enough...")
end
local timer = {STOP = false}
timer.stop = function (...)
@@ -64,7 +65,7 @@ function Timer.timeout(timeout, cb)
Timer_release(t)
local ok, err = pcall(cb)
if not ok then
- log.error('timeout error:', err)
+ Log:ERROR('timeout error:', err)
end
if timer.STOP then
return
@@ -88,7 +89,7 @@ function Timer.at(repeats, cb)
end
local t = Timer_new()
if not t then
- return log.error("timeout error: Create timer class error! memory maybe not enough...")
+ return Log:ERROR("timeout error: Create timer class error! memory maybe not enough...")
end
local timer = { STOP = false }
timer.stop = function (...)
@@ -125,7 +126,7 @@ function Timer.sleep(repeats)
end
local t = Timer_new()
if not t then
- return log.error("timeout error: Create timer class error! memory maybe not enough...")
+ return Log:ERROR("timeout error: Create timer class error! memory maybe not enough...")
end
local timer = {}
timer.current_co = co_self()
diff --git a/lualib/internal/UDP.lua b/lualib/internal/UDP.lua
index 28765084..f0926da3 100644
--- a/lualib/internal/UDP.lua
+++ b/lualib/internal/UDP.lua
@@ -1,6 +1,5 @@
local ti = require "internal.Timer"
local co = require "internal.Co"
-local log = require "log"
local udp = require "udp"
local class = require "class"
@@ -85,4 +84,4 @@ function UDP:close()
-- var_dump(self)
end
-return UDP
\ No newline at end of file
+return UDP
diff --git a/lualib/log/init.lua b/lualib/log/init.lua
deleted file mode 100644
index 3affa571..00000000
--- a/lualib/log/init.lua
+++ /dev/null
@@ -1,142 +0,0 @@
---[[
--- log.lua
---
--- Copyright (c) 2016 rxi
---
--- This library is free software; you can redistribute it and/or modify it
--- under the terms of the MIT license. See LICENSE for details.
--- Modefy by CandyMi In 2018.12.18
-
-log的内部方法包括:
-log.trace(...) 紫色
-log.debug(...) 天蓝色
-log.info(...) 绿色
-log.warn(...) 黄色
-log.error(...) 红色
-log.fatal(...) 粉色
-
-log.usecolor
-默认情况下: 这个为true! 如果你的终端不支持ANSI颜色转义码, 请将它设置为false或者nil.
-
-log.outfile
-将log输出到outfile字符串指定的文件(路径).
-
-log.level
-请参考使用方法相关method
-
---]]
-
-local debug_getinfo = debug.getinfo
-
-local tostring = tostring
-
-local select = select
-
-local ipairs = ipairs
-
-local type = type
-
-local concat = table.concat
-
-local date = os.date
-
-local open = io.open
-
-local ceil = math.ceil
-
-local floor = math.floor
-
-local toint = math.tointeger
-
-local fmt = string.format
-
-
-local log = { _version = "0.1.0" }
-
-log.usecolor = true
-log.outfile = nil
-log.level = "trace"
-
-
-local modes = {
- { name = "trace", color = "\27[34m", },
- { name = "debug", color = "\27[36m", },
- { name = "info", color = "\27[32m", },
- { name = "warn", color = "\27[33m", },
- { name = "error", color = "\27[31m", },
- { name = "fatal", color = "\27[35m", },
-}
-
-
-local levels = {}
-for i, v in ipairs(modes) do
- levels[v.name] = i
-end
-
-
-local round = function(x, increment)
- if not toint(x) then
- increment = increment or 1
- x = x / increment
- return (x > 0 and floor(x + .5) or ceil(x - .5)) * increment
- end
- return x
-end
-
-
-local _tostring = tostring
-
-local tostring = function(...)
- local t = {}
- for i = 1, select('#', ...) do
- local x = select(i, ...)
- if type(x) == "number" then
- x = round(x, .01)
- end
- t[#t + 1] = _tostring(x)
- end
- return concat(t, " ")
-end
-
-
-for i, x in ipairs(modes) do
- local nameupper = x.name:upper()
- log[x.name] = function(...)
-
- -- Return early if we're below the log level
- if i < levels[log.level] then
- return
- end
-
- local msg = tostring(...)
- local info = debug_getinfo(2, "Sl")
- local lineinfo = "[C]:[-1]"
- if info then
- lineinfo = info.short_src .. ":" .. info.currentline
- end
-
- -- Output to console
- print(fmt("%s[%s][%s]%s %s: %s",
- log.usecolor and x.color or "",
- nameupper,
- date("%Y/%m/%d %H:%M:%S"),
- log.usecolor and "\27[0m" or "",
- lineinfo,
- msg))
-
- -- Output to log file
- if log.outfile then
- local fp = open(log.outfile, "a")
- if not fp then
- return log.warn("Cant't write info to "..(log.outfile or ""))
- end
- fp:write(fmt("[%s][%s] %s: %s\n", nameupper, date("%Y/%m/%d %H:%M:%S"), lineinfo, msg))
- fp:close()
- end
-
- end
-
-end
-
-
-return log
diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua
index 021c421a..5b479f6e 100644
--- a/lualib/logging/init.lua
+++ b/lualib/logging/init.lua
@@ -102,8 +102,10 @@ local paths = {}
local Log = class("Log")
function Log:ctor (opt)
- self.path = opt.path
- self.now = Y_m_d()
+ if type(opt) == 'table' then
+ self.path = opt.path
+ self.now = Y_m_d()
+ end
end
-- 常规日志
@@ -144,7 +146,7 @@ function Log:dump(log)
paths[self.path] = file
end
end
- file:write(log)
+ file:write(log):flush()
end
end
diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua
index ab5193f8..7bb920cb 100644
--- a/lualib/protocol/dns.lua
+++ b/lualib/protocol/dns.lua
@@ -1,7 +1,7 @@
local UDP = require "internal.UDP"
local co = require "internal.Co"
local sys = require "sys"
-local log = require "log"
+local log = require "logging"
local prefix = '::ffff:'
@@ -315,4 +315,4 @@ function dns.resolve(domain)
end
-- require "utils"
-- var_dump(dns_cache)
-return dns
\ No newline at end of file
+return dns
diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua
index 958db118..23e61c83 100644
--- a/lualib/protocol/http.lua
+++ b/lualib/protocol/http.lua
@@ -1,8 +1,10 @@
-local log = require "log"
+local log = require "logging"
local sys = require "system"
local tcp = require "internal.TCP"
local wsserver = require "protocol.websocket.server"
+local Log = log:new()
+
local crypt = require "crypt"
local sha1 = crypt.sha1
local base64 = crypt.base64encode
@@ -449,14 +451,14 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
if before_func and (typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE) then
local ok, code, data = pcall(before_func, content)
if not ok then -- before 函数执行出错
- log.error(code)
+ Log:ERROR(code)
sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start))
return sock:close()
end
if code then
if type(code) == "number" then
if code < 200 or code > 500 then
- log.error("before function: Illegal return value")
+ Log:ERROR("before function: Illegal return value")
sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start))
return sock:close()
elseif code == 301 or code == 302 then
@@ -523,7 +525,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
ok, body = pcall(cls, content)
end
if not ok then
- log.error(body)
+ Log:ERROR(body)
statucode = 500
sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start))
return sock:close()
@@ -533,7 +535,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
elseif typ == HTTP_PROTOCOL.WS then
local ok, msg = pcall(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start)
if not ok then
- log.error(msg)
+ Log:ERROR(msg)
return sock:close()
end
return
@@ -546,7 +548,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
end
ok, body, file_type = pcall(cls, './'..path)
if not ok then
- log.error(body)
+ Log:ERROR(body)
statucode = 500
sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start))
return sock:close()
@@ -586,7 +588,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
header[#header+1] = 'Content-Length: '.. #body
end
else
- log.warn('response body not a string type.'..'('..tostring(body)..')')
+ Log:WARN('response body not a string type.'..'('..tostring(body)..')')
body = ''
end
else
diff --git a/lualib/protocol/mqtt/init.lua b/lualib/protocol/mqtt/init.lua
index b3e8c03a..b9e6b777 100644
--- a/lualib/protocol/mqtt/init.lua
+++ b/lualib/protocol/mqtt/init.lua
@@ -10,7 +10,7 @@ local string = require "string"
local calss = require "class"
local tcp = require "internal.TCP"
local Co = require "internal.Co"
-local log = require "log"
+local log = require "logging"
local protocol = require "protocol.mqtt.protocol"
local protocol4 = require "protocol.mqtt.protocol4"
local co = require "internal.Co"
@@ -23,6 +23,8 @@ local make_packet4 = protocol4.make_packet
local parse_packet4 = protocol4.parse_packet
local connack_return_code = protocol4.connack_return_code
+local Log = log:new()
+
-- cache to locals
local type = type
local pairs = pairs
@@ -79,7 +81,7 @@ function client:subscribe(opt, func)
end
local ok, err = pcall(self.handle, nil)
if not ok then
- log.error(err)
+ Log:ERROR(err)
end
return false, 'waiting for the next packet failed'
end
@@ -87,7 +89,7 @@ function client:subscribe(opt, func)
if packet.type == packet_type.PUBLISH then
local ok, err = pcall(self.handle, packet)
if not ok then
- log.error(err)
+ Log:ERROR(err)
end
self:acknowledge(packet)
elseif packet.type == packet_type.PUBACK then
@@ -330,4 +332,4 @@ function client:close( ... )
self.sock = nil
end
-return client
\ No newline at end of file
+return client
diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua
index 61c585c0..386aef91 100644
--- a/lualib/protocol/mysql.lua
+++ b/lualib/protocol/mysql.lua
@@ -1,5 +1,4 @@
local tcp = require "internal.TCP"
-local log = require "log"
local crypt = require "crypt"
local sub = string.sub
diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua
index 132a9b49..937102ac 100644
--- a/lualib/protocol/redis.lua
+++ b/lualib/protocol/redis.lua
@@ -1,10 +1,12 @@
-local log = require "log"
+local log = require "logging"
local class = require "class"
local Co = require "internal.Co"
local tcp = require "internal.TCP"
local table = table
local concat = table.concat
+local Log = log:new()
+
local co_spwan = Co.spwan
local sub = string.sub
@@ -153,7 +155,7 @@ function redis:psubscribe(pattern, func)
if not ok or not msg or not self.sock then
local ok, err = pcall(func, nil)
if not ok then
- log.error(err)
+ Log:ERROR(err)
end
return
end
@@ -163,7 +165,7 @@ function redis:psubscribe(pattern, func)
end
local ok, err = pcall(func, data)
if not ok then
- return log.error(err)
+ return Log:ERROR(err)
end
end
end)
@@ -209,4 +211,4 @@ function redis:close()
end
end
-return redis
\ No newline at end of file
+return redis
diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua
index 58da2d36..b0098e45 100644
--- a/lualib/protocol/websocket/server.lua
+++ b/lualib/protocol/websocket/server.lua
@@ -1,10 +1,12 @@
-local log = require "log"
+local log = require "logging"
local class = require "class"
local co = require "internal.Co"
local wbproto = require "protocol.websocket.protocol"
local _recv_frame = wbproto.recv_frame
local _send_frame = wbproto.send_frame
+local Log = log:new()
+
local co_self = co.self
local co_wait = co.wait
local co_spwan = co.spwan
@@ -55,7 +57,7 @@ function websocket:start()
for _, f in ipairs(write_list) do
local ok, err = pcall(f)
if not ok then
- log.error(err)
+ Log:ERROR(err)
end
end
write_list = {}
@@ -108,8 +110,8 @@ function websocket:start()
end,
-- ping = function (self, data)
-- if self.CLOSE then return end
- -- add_to_queue(write_list, function()
- -- _send_frame(sock, true, 0x9, data, cls.max_payload_len or 65535, cls.send_masked or false)
+ -- add_to_queue(write_list, function()
+ -- _send_frame(sock, true, 0x9, data, cls.max_payload_len or 65535, cls.send_masked or false)
-- end)
-- return wakeup(write_co)
-- end,
@@ -128,7 +130,7 @@ function websocket:start()
local on_close = cls.on_close
local ok, err = pcall(on_open, cls)
if not ok then
- log.error(err)
+ Log:ERROR(err)
return sock:close()
end
while 1 do
@@ -144,12 +146,12 @@ function websocket:start()
if err then
local ok, err = pcall(on_error, cls, err)
if not ok then
- log.error(err)
+ Log:ERROR(err)
end
end
local ok, err = pcall(on_close, cls, data)
if not ok then
- log.error(err)
+ Log:ERROR(err)
end
-- print("读取协程退出了")
return wakeup(write_co)
@@ -174,4 +176,4 @@ function websocket:start()
end
end
-return websocket
\ No newline at end of file
+return websocket
diff --git a/script/main.lua b/script/main.lua
index 11da6b6f..5499cafd 100644
--- a/script/main.lua
+++ b/script/main.lua
@@ -74,4 +74,4 @@ app:static('static', 10)
app:listen("0.0.0.0", 8080)
-- 运行
-app:run()
\ No newline at end of file
+app:run()
From f0c19a263302a37b484d66207a32b589469b9570 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Sun, 28 Apr 2019 17:28:19 +0800
Subject: [PATCH 013/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0redis=E5=8F=82?=
=?UTF-8?q?=E6=95=B0=E5=88=A4=E6=96=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/protocol/redis.lua | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua
index 937102ac..1b2310cf 100644
--- a/lualib/protocol/redis.lua
+++ b/lualib/protocol/redis.lua
@@ -2,9 +2,8 @@ local log = require "logging"
local class = require "class"
local Co = require "internal.Co"
local tcp = require "internal.TCP"
-local table = table
-local concat = table.concat
+local concat = table.concat
local Log = log:new()
local co_spwan = Co.spwan
@@ -98,14 +97,14 @@ local function read_boolean(sock)
end
local function redis_login(sock, auth, db)
- if auth then
+ if type(auth) == 'string' then
sock:send(CMD("AUTH", auth))
local ok, err = read_response(sock)
if not ok then
return nil, err
end
end
- if db then
+ if type(db) == 'number' then
sock:send(CMD("SELECT", db))
local ok, err = read_response(sock)
if not ok then
From 68dbcccf656c5da38824fc1d9d238e04478dbf8b Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Sun, 28 Apr 2019 18:01:56 +0800
Subject: [PATCH 014/956] =?UTF-8?q?=E4=B8=BACache=E5=AE=9E=E7=8E=B0?=
=?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/Cache/init.lua | 234 +++++++++++++++++++++---------------------
script/test_Cache.lua | 10 +-
2 files changed, 125 insertions(+), 119 deletions(-)
diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua
index 337ad994..2192da3c 100644
--- a/lualib/Cache/init.lua
+++ b/lualib/Cache/init.lua
@@ -1,3 +1,4 @@
+local class = require "class"
local log = require "logging"
local Co = require "internal.Co"
local timer = require "internal.Timer"
@@ -7,12 +8,10 @@ local co_self = Co.self
local co_wait = Co.wait
local co_wakeup = Co.wakeup
-local type = type
local ipairs = ipairs
local setmetatable = setmetatable
local table = table
-local unpack = table.unpack
local remove = table.remove
local upper = string.upper
local lower = string.lower
@@ -20,19 +19,6 @@ local splite = string.gmatch
local Log = log:new()
-
--- 默认情况下, 保持50个redis连接
-local MAX, COUNT = 50, 0
-
--- 连接池
-local POOL = {}
-
--- 是否已经初始化
-local INITIALIZATION = false
-
--- session创建函数
-local CREATE_CACHE
-
-- 注册命令
local commands = {
'sismember', 'exists'
@@ -47,136 +33,154 @@ local function in_command(cmd)
return false
end
-local wlist = {}
+local keys = {}
-local function add_wait(co)
- wlist[#wlist+1] = co
+-- 注入函数
+local function in_keys(key)
+ return keys[key]
end
-local function pop_wait()
- return remove(wlist)
+-- 创建Cache函数
+local function CREATE_CACHE(opt)
+ local times = 1
+ local rds
+ while 1 do
+ rds = redis:new(opt)
+ local ok, err = rds:connect()
+ if ok then
+ break
+ end
+ Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接")
+ times = times + 1
+ rds:close()
+ timer.sleep(3)
+ end
+ local ok, ret = rds:cmd("CONFIG", "GET", "TIMEOUT")
+ if not INITIALIZATION and ret[2] ~= '0' then
+ rds:cmd("CONFIG SET", "TIMEOUT", "0")
+ end
+ return rds
end
-local function add_cache(session)
- POOL[#POOL+1] = session
+-- 加入到连接池内
+local function add_cache(self, cache)
+ self.cache_pool[#self.cache_pool+1] = cache
end
-local function pop_cache()
- if #POOL > 0 then
- return remove(POOL)
- end
- if COUNT < MAX then
- COUNT = COUNT + 1
- return CREATE_CACHE()
- end
- add_wait(co_self())
- return co_wait()
+-- 加入到协程池内
+local function add_wait(self, co)
+ self.co_pool[#self.co_pool+1] = co
end
+-- 从连接池内取出一个cache对象
+local function pop_cache(self)
+ if #self.cache_pool > 0 then
+ return remove(self.cache_pool)
+ end
+ if self.current < self.max then
+ self.current = self.current + 1
+ return CREATE_CACHE(self)
+ end
+ add_wait(self, co_self())
+ return co_wait()
+end
-local Cache = setmetatable({}, {__index = function (_, key)
- if not INITIALIZATION then
- return nil, 'Cache尚未初始化'
- end
- if lower(key) == "publish" or lower(key) == "subscribe" or lower(key) == "psubscribe" then
- return nil, 'Cache error: Cache不支持在缓存中直接使用此命令.'
- end
- local cache = pop_cache()
- if in_command(key) then
- return function (_, ...)
- local ok, ret
- while 1 do
- ok, ret = cache[key](cache, ...)
- if ret ~= 'server close!!' then
- break
- end
- cache:close()
- cache = CREATE_CACHE()
- end
- if #wlist > 0 then
- co_wakeup(pop_wait(), cache)
- else
- add_cache(cache)
- end
- return ok, ret
- end
- end
- return function (_, ...)
+-- 弹出一个等待协程
+local function pop_wait(self)
+ return remove(self.co_pool)
+end
+
+-- 构建Cache对象
+local function setmeta(self)
+ keys['count'] = self.count
+ return setmetatable(self, {
+ __index = function(t, key)
+ local f = in_keys(key)
+ if f then
+ return f
+ end
+ if lower(key) == "publish" or lower(key) == "subscribe" or lower(key) == "psubscribe" then
+ return nil, 'Cache error: Cache不支持在缓存中直接使用此命令.'
+ end
+ if in_command(key) then
+ return function (_, ...)
+ local ok, ret
+ local session
+ while 1 do
+ session = pop_cache(t)
+ ok, ret = session[key](session, ...)
+ if ret ~= 'server close!!' then
+ break
+ end
+ session:close()
+ session = nil
+ end
+ local co = pop_wait(t)
+ if co then
+ co_wakeup(co, session)
+ return ok, ret
+ end
+ add_cache(t, session)
+ return ok, ret
+ end
+ end
+ return function (_, ...)
local ok, ret
local keys = {}
for k in splite(key, "([^_]+)") do
keys[#keys+1] = k
end
+ local session
while 1 do
+ session = pop_cache(t)
if #keys > 1 then
- ok, ret = cache:cmd(upper(keys[1]), upper(keys[2]), ...)
+ ok, ret = session:cmd(upper(keys[1]), upper(keys[2]), ...)
else
- ok, ret = cache:cmd(upper(keys[1]), ...)
+ ok, ret = session:cmd(upper(keys[1]), ...)
end
if ret ~= 'server close!!' then
break
end
- cache:close()
- cache = CREATE_CACHE()
+ session:close()
+ session = nil
end
- if #wlist > 0 then
- co_wakeup(pop_wait(), cache)
- else
- add_cache(cache)
+ local co = pop_wait(t)
+ if co then
+ co_wakeup(co, session)
+ return ok, ret
end
+ add_cache(t, session)
return ok, ret
end
-end})
-
--- 初始化
-function Cache.init(opt)
- if INITIALIZATION then
- return nil, "Cache已经初始化."
- end
-
- assert(type(opt) == 'table', "Cache error: 错误的Cache配置文件.")
-
- assert(type(opt.host) == 'string' and opt.host ~= '', "Cache error: 异常的主机名.")
-
- assert(type(opt.port) == 'number' and opt.port > 0 and opt.port <= 65535, "Cache error: 异常的端口.")
-
- assert(not opt.auth or type(opt.auth) == 'string' , "Cache error: 异常的auth.")
-
- assert(not opt.db or type(opt.db) == 'number' and opt.db >= 0 and opt.db <= 15, "Cache error: 异常的db.")
+ end}) == self
+end
- if type(opt.max) == 'number' and opt.max > 0 then
- MAX = opt.max
- end
+local Cache = class("Cache")
+
+function Cache:ctor (opt)
+ self.host = opt.host
+ self.port = opt.port
+ self.db = opt.db
+ self.auth = opt.auth
+ self.max = opt.max or 50
+ self.current = 0
+ -- 连接池
+ self.cache_pool = {}
+ -- 协程池
+ self.co_pool = {}
+end
- CREATE_CACHE = function ()
- local times = 1
- local rds
- while 1 do
- rds = redis:new(opt)
- local ok, err = rds:connect()
- if ok then
- break
- end
- Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接")
- times = times + 1
- rds:close()
- timer.sleep(3)
- end
- local ok, ret = rds:cmd("CONFIG", "GET", "TIMEOUT")
- if not INITIALIZATION and ret[2] ~= '0' then
- rds:cmd("CONFIG SET", "TIMEOUT", "0")
- end
- return rds
- end
- add_cache(CREATE_CACHE())
- INITIALIZATION = true
- COUNT = COUNT + 1
- return true
+function Cache:connect ()
+ if not self.INITIALIZATION then
+ add_cache(self, pop_cache(self))
+ self.INITIALIZATION = true
+ return setmeta(self)
+ end
+ return true
end
--- 连接池数量
-function Cache.count()
- return #POOL
+function Cache:count()
+ return self.current, self.max
end
return Cache
diff --git a/script/test_Cache.lua b/script/test_Cache.lua
index 1832c693..2c259445 100644
--- a/script/test_Cache.lua
+++ b/script/test_Cache.lua
@@ -3,16 +3,18 @@ local Cache = require "Cache"
local Co = require "internal.Co"
local timer = require "internal.Timer"
require "utils"
+
local opt = {
host = "localhost",
port = 6379,
auth = nil,
- db = nil,
+ db = 1,
max = 1,
}
Co.spwan(function ( ... )
- local ok, err = Cache.init(opt)
+ local Cache = Cache:new(opt)
+ local ok, err = Cache:connect()
if not ok then
return print(err)
end
@@ -114,7 +116,7 @@ Co.spwan(function ( ... )
local ok, ret = Cache:script_load("return 10086")
print(ok); var_dump(ret)
- local sha = ret
+ local sha = ret
local ok, ret = Cache:script_exists(sha)
print(ok); var_dump(ret)
@@ -126,5 +128,5 @@ Co.spwan(function ( ... )
-- 其它一些特殊方法支持
-- type, move, rename, keys, randomkey等等
-
+ print(Cache:count())
end)
From 11fdae72a463169e1fac42e2bb124f87976bfb7e Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Sun, 28 Apr 2019 18:29:47 +0800
Subject: [PATCH 015/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0logging?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/logging/init.lua | 33 +++++++++++++++------------------
1 file changed, 15 insertions(+), 18 deletions(-)
diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua
index 5b479f6e..3e95e272 100644
--- a/lualib/logging/init.lua
+++ b/lualib/logging/init.lua
@@ -22,7 +22,7 @@ local modf = math.modf
-- 格式化时间: [年-月-日 时:分:秒,毫秒]
local function fmt_Y_m_d_H_M_S()
local ts, f = modf(now())
- f = format("%3.0f", f * 1e3)
+ f = format("%03.0f", f * 1e3)
return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', f, ']'})
end
@@ -43,29 +43,26 @@ local function table_format(t)
while 1 do
local mt = getmetatable(t)
for key, value in pairs(t) do
+ local k, v
if type(key) == 'number' then
- if type(value) == 'table' then
- if t ~= value then
- tab[#tab+1] = concat({'[', key, ']', '=', '{', concat(t, ", "), '}'})
- end
- elseif type(value) == 'string' then
- tab[#tab+1] = concat({'[', key, ']', '="', tostring(value), '"'})
- else
- tab[#tab+1] = concat({'[', key, ']', '=', tostring(value)})
- end
+ k = concat({'[', key, ']'})
else
- if type(value) == 'table' then
- if t ~= value then
- tab[#tab+1] = concat({'["', key, '"]', '=', '{', concat(t, ", "), '}'})
- end
- elseif type(value) == 'string' then
- tab[#tab+1] = concat({'["', key, '"]', '="', tostring(value), '"'})
+ k = concat({'["', key, '"]'})
+ end
+ if type(value) == 'table' then
+ if t ~= value then
+ v = table_format(value)
else
- tab[#tab+1] = concat({'["', key, '"]', '=', tostring(value)})
+ v = tostring(value)
end
+ elseif type(value) == 'string' then
+ v = concat({'"', value, '"'})
+ else
+ v = tostring(value)
end
+ tab[#tab+1] = concat({k, '=', v})
end
- if not mt then
+ if not mt or mt == t then
break
end
t = mt
From c1d67b79f5b4632416b42bce551c67b802769e03 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Mon, 29 Apr 2019 02:57:25 +0800
Subject: [PATCH 016/956] =?UTF-8?q?=E4=BC=98=E5=8C=96DB=E5=A2=9E=E5=8A=A0?=
=?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B=E5=88=9D=E5=A7=8B=E5=8C=96=E3=80=81?=
=?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=96=B9=E6=B3=95,=20=E5=A2=9E=E5=8A=A0?=
=?UTF-8?q?=E4=BF=AE=E6=94=B9DB=5Ftest.lua=E7=A4=BA=E4=BE=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/DB/init.lua | 300 +++++++++++++++++---------------------
lualib/protocol/mysql.lua | 7 +-
script/test_DB.lua | 55 ++++---
3 files changed, 164 insertions(+), 198 deletions(-)
diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua
index 74680e16..01e63062 100644
--- a/lualib/DB/init.lua
+++ b/lualib/DB/init.lua
@@ -1,6 +1,7 @@
local mysql = require "protocol.mysql"
local timer = require "internal.Timer"
local co = require "internal.Co"
+local class = require "class"
local log = require "logging"
local Log = log:new()
@@ -26,8 +27,6 @@ local remove = table.remove
local concat = table.concat
local unpack = table.unpack
-local os_time = os.time
-
local SELECT = "SELECT"
local INSERT = "INSERT INTO"
@@ -64,58 +63,53 @@ local GROUPBY = "GROUP BY"
local COMMA = ", "
-local INITIALIZATION
-
--- 最大DB连接数量
-local MAX, COUNT = 50, 0
-
-- 空闲连接时间
-local WAIT_TIMEOUT = 2592000
+local WAIT_TIMEOUT = 31104000
-- 数据库连接创建函数
-local DB_CREATE
-
--- 等待db对象的协程列表
-local wlist = {}
-
-local function add_wait(co)
- wlist[#wlist+1] = co
+local function DB_CREATE (opt)
+ local times = 1
+ local db
+ while 1 do
+ db = mysql:new()
+ local connect, err = db:connect(opt)
+ if connect then
+ break
+ end
+ db:close()
+ Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接")
+ times = times + 1
+ timer.sleep(3)
+ end
+ db:query(fmt('SET wait_timeout=%s', tostring(WAIT_TIMEOUT)))
+ db:query(fmt('SET interactive_timeout=%s', tostring(WAIT_TIMEOUT)))
+ db:query(fmt('SET wait_timeout=%s', tostring(WAIT_TIMEOUT)))
+ -- Log:DEBUG(db:query("show session variables where variable_name ='wait_timeout' or variable_name = 'interactive_timeout'"))
+ return db
end
-local function pop_wait()
- return remove(wlist)
+local function add_wait(self, co)
+ self.co_pool[#self.co_pool + 1] = co
end
--- 数据库连接池
-local POOL = {}
+local function pop_wait(self)
+ return remove(self.co_pool)
+end
-local function add_db(db)
- POOL[#POOL + 1] = { session = db, ttl = os_time() }
+local function add_db(self, db)
+ self.db_pool[#self.db_pool + 1] = db
end
-- 负责创建连接/加入等待队列
-local function get_db()
- if #POOL > 0 then
- while 1 do
- local db = remove(POOL)
- if not db then break end -- 连接池内已经没有连接了
- if db.ttl > os_time() - WAIT_TIMEOUT then
- return db.session
- end
- db.session:close()
- COUNT = COUNT - 1
- end
+local function pop_db(self)
+ if #self.db_pool > 0 then
+ return remove(self.db_pool)
end
- if COUNT < MAX then
- COUNT = COUNT + 1
- local db = DB_CREATE()
- if db then
- return db
- end
- COUNT = COUNT - 1
- -- 连接失败或者其他情况, 将等待其他协程唤醒; 保证公平竞争数据库连接
+ if self.current < self.max then
+ self.current = self.current + 1
+ return DB_CREATE(self)
end
- add_wait(co_self())
+ add_wait(self, co_self())
return co_wait()
end
@@ -147,43 +141,6 @@ local function format_value4(t)
return fmt("%s %s %s '%s'", unpack(t))
end
--- 执行
-local function execute(query)
- if query.SELECT then
- assert(query.FROM and query.WHERE, "查询语句必须使用from方法与where条件.")
- end
- if query.DELETE then
- assert(query.WHERE, "删除语句请加上where条件")
- end
- if query.INSERT then
- assert(query.FILEDS and query.VALUES, "插入语句请加上fields与values")
- end
- if query.UPDATE then
- assert(query.WHERE, "更新语句请加上where条件")
- end
- local QUERY = concat(query, " ")
- -- print(QUERY)
- local db, ret, err
- while 1 do
- db = get_db()
- if db then
- ret, err = db:query(QUERY)
- if db.state then
- break
- end
- Log:ERROR(err)
- db:close()
- db, ret, err = nil
- end
- end
- if #wlist > 0 then
- co_wakeup(pop_wait(), db)
- else
- add_db(db)
- end
- return ret, err
-end
-
local function limit(query, limit1, limit2)
local t1 = type(limit1)
local t2 = type(limit2)
@@ -332,10 +289,6 @@ local function fields(query, fields)
end
-- 插入语句专用函数 --
-
-
-
-
-- 更新语句专用 --
local function set(query, values)
local tpy = type(values)
@@ -350,60 +303,62 @@ local function set(query, values)
end
-- 更新语句专用 --
-
-
-local DB = {}
-
--- 初始化数据库
-function DB.init(opt)
- if INITIALIZATION then
- return nil, "DB已经初始化."
+-- 执行
+local function execute(query)
+ -- Log:DEBUG(query)
+ if query.SELECT then
+ assert(query.FROM and query.WHERE, "查询语句必须使用from方法与where条件.")
end
- if type(opt) ~= 'table' then
- return nil, '错误的DB配置文件.'
+ if query.DELETE then
+ assert(query.WHERE, "删除语句请加上where条件")
end
- if type(opt.max) == 'number' then
- MAX = opt.max
+ if query.INSERT then
+ assert(query.FILEDS and query.VALUES, "插入语句请加上fields与values")
end
- local config = {
- host = opt.host or 'localhost',
- port = opt.port or 3306,
- database = opt.database,
- user = opt.user,
- password = opt.password,
- }
- DB_CREATE = function(...)
- local times = 1
- local db
- while 1 do
- db = mysql:new()
- local connect, err = db:connect(config)
- if connect then
- break
- end
- Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接")
- db:close()
- times = times + 1
- timer.sleep(3)
- end
- db:query(fmt('SET wait_timeout=%s', tostring(WAIT_TIMEOUT)))
- db:query(fmt('SET interactive_timeout=%s', tostring(WAIT_TIMEOUT)))
- return db
+ if query.UPDATE then
+ assert(query.WHERE, "更新语句请加上where条件")
end
- add_db(get_db())
- INITIALIZATION = true
- return true
+ local QUERY = concat(query, " ")
+ Log:DEBUG(QUERY)
+ local self = query.self
+ query.self = nil
+ return self:query(QUERY)
end
+local DB = class("DB")
+
+function DB:ctor(opt)
+ self.host = opt.host
+ self.port = opt.port
+ self.username = opt.username
+ self.password = opt.password
+ self.database = opt.database
+ self.max = opt.max or 50
+ self.current = 0
+ -- 协程池
+ self.co_pool = {}
+ -- 连接池
+ self.db_pool = {}
+end
+
+function DB:connect ()
+ if not self.INITIALIZATION then
+ add_db(self, pop_db(self))
+ self.INITIALIZATION = true
+ return self.INITIALIZATION
+ end
+ return self.INITIALIZATION
+end
-- 查询语句
-function DB.select(fields)
- if not INITIALIZATION then
+function DB:select(fields)
+ if not self.INITIALIZATION then
return nil, "DB尚未初始化"
end
local tpy = type(fields)
assert(tpy == "string" or tpy == "table", "错误的字段类型(fields):"..tostring(fields))
local query = {
+ self = self,
[1] = SELECT,
SELECT = true,
from = from,
@@ -424,31 +379,16 @@ function DB.select(fields)
return query
end
--- 插入语句
-function DB.insert(table_name)
- if not INITIALIZATION then
- return nil, "DB尚未初始化"
- end
- local tpy = type(table_name)
- assert(tpy == "string", "错误的表名(table_name):"..tostring(table_name))
- return {
- [1] = INSERT,
- [2] = table_name,
- INSERT = true,
- fields = fields,
- values = values,
- execute = execute,
- }
-end
-- 更新语句
-function DB.update(table_name)
- if not INITIALIZATION then
+function DB:update(table_name)
+ if not self.INITIALIZATION then
return nil, "DB尚未初始化"
end
local tpy = type(table_name)
assert(tpy == "string" or tpy == "tables", "错误的表名(table_name):"..tostring(table_name))
return {
+ self = self,
[1] = UPDATE,
[2] = table_name,
UPDATE = true,
@@ -459,14 +399,34 @@ function DB.update(table_name)
}
end
+
+-- 插入语句
+function DB:insert(table_name)
+ if not self.INITIALIZATION then
+ return nil, "DB尚未初始化"
+ end
+ local tpy = type(table_name)
+ assert(tpy == "string", "错误的表名(table_name):"..tostring(table_name))
+ return {
+ self = self,
+ [1] = INSERT,
+ [2] = table_name,
+ INSERT = true,
+ fields = fields,
+ values = values,
+ execute = execute,
+ }
+end
+
-- 删除语句
-function DB.delete(table_name)
- if not INITIALIZATION then
+function DB:delete(table_name)
+ if not self.INITIALIZATION then
return nil, "DB尚未初始化"
end
local tpy = type(table_name)
assert(tpy == "string" or tpy == "tables", "错误的表名(table_name):"..tostring(table_name))
return {
+ self = self,
[1] = DELETE,
[2] = FROM,
[3] = table_name,
@@ -478,35 +438,37 @@ function DB.delete(table_name)
}
end
--- 原始SQL
-function DB.query(query)
- if not INITIALIZATION then
- return nil, "DB尚未初始化"
- end
- assert(type(query) == 'string' and query ~= '' , "原始SQL类型错误(query):"..tostring(query))
- local db, ret, err
- while 1 do
- db = get_db()
- if db then
- ret, err = db:query(query)
- if db.state then
- break
- end
- Log:ERROR(err)
- db:close()
- db, ret, err = nil
- end
- end
- if #wlist > 0 then
- co_wakeup(pop_wait(), db)
- else
- add_db(db)
- end
+-- 原始查询语句
+function DB:query(query)
+ if not self.INITIALIZATION then
+ return nil, "DB尚未初始化"
+ end
+ assert(type(query) == 'string' and query ~= '' , "原始SQL类型错误(query):"..tostring(query))
+ local db, ret, err
+ while 1 do
+ db = pop_db(self)
+ if db then
+ ret, err = db:query(query)
+ if db.state then
+ break
+ end
+ Log:ERROR(err)
+ db:close()
+ self.current = self.current - 1
+ db, ret, err = nil, nil, nil
+ end
+ end
+ local co = pop_wait(self)
+ if co then
+ co_wakeup(co, db)
return ret, err
+ end
+ add_db(self, db)
+ return ret, err
end
-function DB.count( ... )
- return #POOL
+function DB:count()
+ return self.current, self.max, #self.co_pool, #self.db_pool
end
return DB
diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua
index 386aef91..507a4027 100644
--- a/lualib/protocol/mysql.lua
+++ b/lualib/protocol/mysql.lua
@@ -375,7 +375,7 @@ function MySQL.connect(self, opts)
self.compact = opts.compact_arrays
local database = opts.database or ""
- local user = opts.user or ""
+ local username = opts.username or ""
local host = opts.host
if not host then
@@ -383,9 +383,6 @@ function MySQL.connect(self, opts)
end
local port = opts.port or 3306
- if not pool then
- pool = user .. ":" .. database .. ":" .. host .. ":" .. port
- end
local ok = sock:connect(host, port)
if not ok then
@@ -475,7 +472,7 @@ function MySQL.connect(self, opts)
client_flags,
self._max_packet_size,
strrep("\0", 24), -- TODO: add support for charset encoding
- user,
+ username,
token,
database)
diff --git a/script/test_DB.lua b/script/test_DB.lua
index 475d6f44..a011acfa 100644
--- a/script/test_DB.lua
+++ b/script/test_DB.lua
@@ -1,17 +1,24 @@
+local log = require "logging"
+local cf = require "cf"
local DB = require "DB"
-local co = require "internal.Co"
+local Log = log:new()
+
require "utils"
-local ok, err = DB.init({
- host = "localhost",
- port = 3306,
- database = "test",
- user = "root",
- password = "123456789"
- })
+local db = DB:new {
+ host = 'localhost',
+ port = 3306,
+ database = 'test',
+ username = 'root',
+ password = '123456789',
+ max = 1,
+}
+
+local ok = db:connect()
if not ok then
- return print("连接mysql 失败: "..err)
+ return print("连接mysql失败")
end
+print("连接成功")
--[[
复制下面语句到任意管理工具即可导入测试表进行测试
@@ -25,19 +32,19 @@ end
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `name` varchar(255) NOT NULL,
- `user` varchar(255) NOT NULL,
- `passwd` varchar(255) NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
+ `user` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
+ `passwd` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
PRIMARY KEY (`id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
+ ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
SET FOREIGN_KEY_CHECKS = 1;
--]]
-- 插入语句示例
-co.spwan(function ( ... )
- local ret, err = DB.insert("user")
+cf.fork(function ( ... )
+ local ret, err = db:insert("user")
:fields({"name", "user", "passwd"})
:values({
{"candy", "root", "123456789"},
@@ -53,8 +60,8 @@ co.spwan(function ( ... )
end)
-- 查询语句示例
-co.spwan(function ( ... )
- local ret, err = DB.select({"id", "name", "user", "passwd"})
+cf.fork(function ( ... )
+ local ret, err = db:select({"id", "name", "user", "passwd"})
:from({"user"})
:where({
{"id", "!=", "0"},
@@ -91,8 +98,8 @@ co.spwan(function ( ... )
end)
-- 更新语句示例
-co.spwan(function ( ... )
- local ret, err = DB.update("user")
+cf.fork(function ( ... )
+ local ret, err = db:update("user")
:set({
{"name", "=", "管理员"},
{"user", "=", "Administrator"},
@@ -112,8 +119,8 @@ co.spwan(function ( ... )
end)
-- 删除语句示例
-co.spwan(function ( ... )
- local ret, err = DB.delete("user")
+cf.fork(function ( ... )
+ local ret, err = db:delete("user")
:where({
{"id", ">", 1},
})
@@ -129,11 +136,11 @@ co.spwan(function ( ... )
end)
-co.spwan(function ( ... )
- local ret, err = DB.query("show variables like 'wait_timeout'")
+cf.fork(function ( ... )
+ local ret, err = db:query("show variables like 'wait_timeout'")
if not ret then
return print(err)
end
var_dump(ret)
-end)
\ No newline at end of file
+end)
From 4b30b24f13d9f896d9272e5cb2f160082c51bf7d Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Mon, 29 Apr 2019 02:58:08 +0800
Subject: [PATCH 017/956] =?UTF-8?q?=E4=BC=98=E5=8C=96DB=E5=A2=9E=E5=8A=A0?=
=?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B=E5=88=9D=E5=A7=8B=E5=8C=96=E3=80=81?=
=?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=96=B9=E6=B3=95,=20=E5=A2=9E=E5=8A=A0?=
=?UTF-8?q?=E4=BF=AE=E6=94=B9DB=5Ftest.lua=E7=A4=BA=E4=BE=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/DB/init.lua | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua
index 01e63062..7785190a 100644
--- a/lualib/DB/init.lua
+++ b/lualib/DB/init.lua
@@ -319,7 +319,7 @@ local function execute(query)
assert(query.WHERE, "更新语句请加上where条件")
end
local QUERY = concat(query, " ")
- Log:DEBUG(QUERY)
+ -- Log:DEBUG(QUERY)
local self = query.self
query.self = nil
return self:query(QUERY)
From 550921836c6f0169bf5bf7149bd968bc0357f63f Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Wed, 1 May 2019 16:58:17 +0800
Subject: [PATCH 018/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9http=E5=8D=8F?=
=?UTF-8?q?=E8=AE=AE=E4=BB=85=E5=9C=A8options=E4=B8=8Ehead=E6=89=8D?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=E8=B7=A8=E5=9F=9F=E6=A0=87=E5=BF=97=E5=A4=B4?=
=?UTF-8?q?=E9=83=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/protocol/http.lua | 20 +-------------------
1 file changed, 1 insertion(+), 19 deletions(-)
diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua
index 23e61c83..9e165c83 100644
--- a/lualib/protocol/http.lua
+++ b/lualib/protocol/http.lua
@@ -310,11 +310,6 @@ local function ERROR_RESPONSE(http, code, path, ip, forword, method, speed)
HTTP_DATE(),
'Origin: *',
'Allow: GET, POST, PUT, HEAD, OPTIONS',
- 'Allow: GET, POST, PUT, HEAD, OPTIONS',
- 'Access-Control-Allow-Origin: *',
- 'Access-Control-Allow-Headers: *',
- 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS',
- 'Access-Control-Max-Age: 86400',
'Connection: close',
'server: ' .. (http.__server or SERVER),
}, CRLF), CRLF2})
@@ -440,6 +435,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
'Access-Control-Allow-Origin: *',
'Access-Control-Allow-Headers: *',
'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS',
+ 'Access-Control-Allow-Credentials: true',
'Access-Control-Max-Age: 86400',
'Connection: keep-alive',
'server: ' .. (server or SERVER),
@@ -465,12 +461,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
http:tolog(code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start)
sock:send(concat({
REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(),
- 'Origin: *',
- 'Allow: GET, POST, PUT, HEAD, OPTIONS',
- 'Access-Control-Allow-Origin: *',
- 'Access-Control-Allow-Headers: *',
- 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS',
- 'Access-Control-Max-Age: 86400',
'Connection: close',
'server: ' .. (server or SERVER),
'Location: ' .. (data or "https://github.com/CandyMi/core_framework"),
@@ -484,10 +474,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(),
'Origin: *',
'Allow: GET, POST, PUT, HEAD, OPTIONS',
- 'Access-Control-Allow-Origin: *',
- 'Access-Control-Allow-Headers: *',
- 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS',
- 'Access-Control-Max-Age: 86400',
'server: ' .. (server or SERVER),
'Connection: close',
'Content-Type: ' .. REQUEST_MIME_RESPONSE('html'),
@@ -571,10 +557,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
header[#header+1] = HTTP_DATE()
header[#header+1] = 'Origin: *'
header[#header+1] = 'Allow: GET, POST, PUT, HEAD, OPTIONS'
- header[#header+1] = 'Access-Control-Allow-Origin: *'
- header[#header+1] = 'Access-Control-Allow-Headers: *'
- header[#header+1] = 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS'
- header[#header+1] = 'Access-Control-Max-Age: 86400'
header[#header+1] = 'server: ' .. (server or SERVER)
local Connection = 'Connection: keep-alive'
if not HEADER['Connection'] or lower(HEADER['Connection']) == 'close' then
From b11abfeeca79c7c2d57f02ccb7b78dde6e7d0ca1 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Thu, 2 May 2019 23:08:33 +0800
Subject: [PATCH 019/956] =?UTF-8?q?=E8=B0=83=E6=95=B4lcript=E7=9A=84C?=
=?UTF-8?q?=E5=BA=93=E5=BC=95=E7=94=A8=E6=96=B9=E5=BC=8F,=20=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E4=B8=80=E4=B8=AA=E4=B8=AD=E9=97=B4=E6=96=87=E4=BB=B6?=
=?UTF-8?q?=E4=BD=9C=E4=B8=BA=E6=98=BE=E7=A4=BA=E5=BC=95=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
luaclib/Makefile | 4 +-
luaclib/src/lcrypt.c | 18 ++++-----
lualib/crypt/init.lua | 94 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 105 insertions(+), 11 deletions(-)
create mode 100644 lualib/crypt/init.lua
diff --git a/luaclib/Makefile b/luaclib/Makefile
index 83ac640b..72c96936 100644
--- a/luaclib/Makefile
+++ b/luaclib/Makefile
@@ -19,7 +19,7 @@ build :
$(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
$(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
- $(CC) -o crypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
+ $(CC) -o lcrypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
### 以下为预留第三方库编译位置 ###
cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库
cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库
@@ -32,7 +32,7 @@ rebuild :
$(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
$(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
- $(CC) -o crypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
+ $(CC) -o lcrypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore
### 以下为预留第三方库编译位置 ###
cd src/lhttpparser && rm -rf *.o *.so && make rebuild # 增加PicoHTTPParser库
cd src/lcjson && rm -rf *.o *.so && make rebuild # 增加cjson库
diff --git a/luaclib/src/lcrypt.c b/luaclib/src/lcrypt.c
index b3fdcca2..11085430 100644
--- a/luaclib/src/lcrypt.c
+++ b/luaclib/src/lcrypt.c
@@ -15,7 +15,7 @@ typedef struct {
uint32_t count[2];
uint8_t buffer[64];
} SHA1_CTX;
-
+
#define SHA1_DIGEST_SIZE 20
@@ -469,7 +469,7 @@ lhmac_sha1(lua_State *L) {
return 1;
}
-static void
+static void
des_main_ks( uint32_t SK[32], const uint8_t key[8] ) {
int i;
uint32_t X, Y, T;
@@ -538,7 +538,7 @@ des_main_ks( uint32_t SK[32], const uint8_t key[8] ) {
/* DES 64-bit block encryption/decryption */
-static void
+static void
des_crypt( const uint32_t SK[32], const uint8_t input[8], uint8_t output[8] ) {
uint32_t X, Y, T;
@@ -762,13 +762,13 @@ static const uint32_t k[64] = {
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 ,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 ,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 };
-
+
// r specifies the per-round shift amounts
static const uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
-
+
// leftrotate function definition
#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
@@ -776,7 +776,7 @@ static void
digest_md5(uint32_t w[16], uint32_t result[4]) {
uint32_t a, b, c, d, f, g, temp;
int i;
-
+
a = 0x67452301u;
b = 0xefcdab89u;
c = 0x98badcfeu;
@@ -791,7 +791,7 @@ digest_md5(uint32_t w[16], uint32_t result[4]) {
g = (5*i + 1) % 16;
} else if (i < 48) {
f = b ^ c ^ d;
- g = (3*i + 5) % 16;
+ g = (3*i + 5) % 16;
} else {
f = c ^ (b | (~d));
g = (7*i) % 16;
@@ -1176,7 +1176,7 @@ lxor_str(lua_State *L) {
}
LUAMOD_API int
-luaopen_crypt(lua_State *L) {
+luaopen_lcrypt(lua_State *L) {
luaL_checkversion(L);
static int init = 0;
if (!init) {
@@ -1205,4 +1205,4 @@ luaopen_crypt(lua_State *L) {
};
luaL_newlib(L, crypt_libs);
return 1;
-}
\ No newline at end of file
+}
diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua
new file mode 100644
index 00000000..26b0b720
--- /dev/null
+++ b/lualib/crypt/init.lua
@@ -0,0 +1,94 @@
+local CRYPT = require "lcrypt"
+
+local sha1 = CRYPT.sha1
+local xor_str = CRYPT.xor_str
+
+local randomkey = CRYPT.randomkey
+local hashkey = CRYPT.hashkey
+
+local hmac_sha1 = CRYPT.hmac_sha1
+local hmac_hash = CRYPT.hmac_hash
+
+local hmac64 = CRYPT.hmac64
+local hmac64_md5 = CRYPT.hmac64_md5
+
+local base64encode = CRYPT.base64encode
+local base64decode = CRYPT.base64decode
+
+local hexencode = CRYPT.hexencode
+local hexdecode = CRYPT.hexdecode
+
+local desencode = CRYPT.desencode
+local desdecode = CRYPT.desdecode
+
+local dhsecret = CRYPT.dhsecret
+local dhexchange = CRYPT.dhexchange
+
+
+local crypt = {}
+
+function crypt.sha1(...)
+ return sha1(...)
+end
+
+function crypt.xor_str (...)
+ return CRYPT(...)
+end
+
+function crypt.randomkey(...)
+ return randomkey(...)
+end
+
+function crypt.hashkey (...)
+ return hashkey(...)
+end
+
+function crypt.hmac_sha1 (...)
+ return hmac_sha1(...)
+end
+
+function crypt.hmac_hash (...)
+ return hmac_hash(...)
+end
+
+function crypt.hmac64 (...)
+ return hmac64(...)
+end
+
+function crypt.hmac64_md5 (...)
+ return hmac64_md5(...)
+end
+
+function crypt.base64encode (...)
+ return base64encode(...)
+end
+
+function crypt.base64decode (...)
+ return base64decode(...)
+end
+
+function crypt.hexencode (...)
+ return hexencode(...)
+end
+
+function crypt.hexdecode (...)
+ return hexdecode(...)
+end
+
+function crypt.desencode (...)
+ return desencode(...)
+end
+
+function crypt.desdecode (...)
+ return desdecode(...)
+end
+
+function crypt.dhsecret (...)
+ return dhsecret(...)
+end
+
+function crypt.dhexchange (...)
+ return dhexchange(...)
+end
+
+return crypt
From 168df63cd19c6469f0c6d2e0dd1b622c147b949d Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Thu, 2 May 2019 23:10:15 +0800
Subject: [PATCH 020/956] =?UTF-8?q?=E8=B0=83=E6=95=B4lcript=E7=9A=84C?=
=?UTF-8?q?=E5=BA=93=E5=BC=95=E7=94=A8=E6=96=B9=E5=BC=8F,=20=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E4=B8=80=E4=B8=AA=E4=B8=AD=E9=97=B4=E6=96=87=E4=BB=B6?=
=?UTF-8?q?=E4=BD=9C=E4=B8=BA=E6=98=BE=E7=A4=BA=E5=BC=95=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/crypt/init.lua | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua
index 26b0b720..315f0b5f 100644
--- a/lualib/crypt/init.lua
+++ b/lualib/crypt/init.lua
@@ -32,7 +32,7 @@ function crypt.sha1(...)
end
function crypt.xor_str (...)
- return CRYPT(...)
+ return xor_str(...)
end
function crypt.randomkey(...)
From 015bf1de287e10ff3bfcf302c309d80783544231 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 3 May 2019 00:04:54 +0800
Subject: [PATCH 021/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A5=E5=BF=97?=
=?UTF-8?q?=E7=9A=84=E4=B8=80=E4=B8=AA=E6=AD=BB=E5=BE=AA=E7=8E=AFbug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/logging/init.lua | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua
index 3e95e272..a83f8f9d 100644
--- a/lualib/logging/init.lua
+++ b/lualib/logging/init.lua
@@ -81,7 +81,7 @@ local function fmt(...)
else
tab[#tab+1]= tostring(arg)
end
- if index == len then
+ if index >= len then
break
end
index = index + 1
From 7d3870e0359dd61d22a3bc95cef9fed8e01be731 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 3 May 2019 01:11:59 +0800
Subject: [PATCH 022/956] =?UTF-8?q?=E6=96=B0=E5=A2=9ECookie=E6=89=A9?=
=?UTF-8?q?=E5=B1=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/httpd/Cookie.lua | 122 +++++++++++++++++++++++++++++++++++++++
lualib/httpd/init.lua | 17 +++++-
lualib/protocol/http.lua | 25 +++++++-
3 files changed, 162 insertions(+), 2 deletions(-)
create mode 100644 lualib/httpd/Cookie.lua
diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua
new file mode 100644
index 00000000..84405982
--- /dev/null
+++ b/lualib/httpd/Cookie.lua
@@ -0,0 +1,122 @@
+local Co = require "internal.Co"
+local co_self = Co.self
+
+local crypt = require "crypt"
+local xore_str = crypt.xor_str
+local hexencode = crypt.hexencode
+local hexdecode = crypt.hexdecode
+
+local type = type
+local assert = assert
+local ipairs = ipairs
+local os_date = os.date
+local os_time = os.time
+local concat = table.concat
+local splite = string.gmatch
+
+-- 默认密匙
+local secure = 'http://github.com/candymi/core_framework'
+
+-- 加密Cookie Value
+local function encode_value (value)
+ return hexencode(xore_str(value, secure)):upper()
+end
+
+-- 解密Cookie Value
+local function decode_value (value)
+ return xore_str(hexdecode(value:lower()), secure)
+end
+
+-- 当前协程注册的cookie
+local Cookie = {
+ client = {},
+ server = {},
+}
+
+function Cookie.setSecure (sec)
+ if type(sec) == 'string' and sec ~= '' then
+ secure = sec
+ end
+end
+
+-- 设置Cookie
+function Cookie.setCookie (name, value, expires, notall, https)
+ assert(type(name) == 'string' and key ~= '', '错误的Cookie Key, 请检查参数有效性')
+ assert(type(value) == 'string' or type(value) == 'number', '错误的Cookie Value, 请检查参数有效性')
+ assert(not expires or expires > os_time(), '错误的Cookie Expires, 请检查参数有效性')
+ local co = co_self()
+ local cs = Cookie.server[co]
+ if not cs then
+ cs = {}
+ Cookie.server[co] = cs
+ end
+ cs[#cs+1] = {
+ name = name,
+ value = value,
+ expires = expires,
+ path = '/',
+ httponly = notall and 'HttpOnly',
+ secure = https and 'Secure'
+ }
+end
+
+-- 获取置顶Cookie字段
+function Cookie.getCookie (name)
+ local co = co_self
+ local cs = Cookie.client[co]
+ local value = cs.client[name]
+ if not value then
+ return
+ end
+ return decode_value(value)
+end
+
+-- 对cookie进行反序列化
+function Cookie.deserialization (cs)
+ if type(cs) ~= 'string' or cs == '' then
+ return
+ end
+ local co = co_self()
+ local Cookies = {}
+ for name, value in splite(cs, '([^ ;]+)=([^ ;]+)') do
+ Cookies[name] = value
+ end
+ Cookie.client[co] = Cookies
+ -- local log = require 'logging'
+ -- local Log = log:new()
+ -- Log:DEBUG('反序列化后的cookie', Cookies)
+end
+
+-- 对cookie进行序列化
+function Cookie.serialization ()
+ local co = co_self()
+ local cs = Cookie.server[co]
+ if not cs then
+ return {}
+ end
+ local tab = {}
+ for _, cookie in ipairs(cs) do
+ local t = {}
+ t[#t+1] = concat({cookie.name, '=', encode_value(cookie.value)})
+ t[#t+1] = cookie.expires and concat({'expires', '=', os_date("%a, %d %b %Y %X GMT", cookie.expires)})
+ t[#t+1] = concat({'path', '=', cookie.path})
+ t[#t+1] = cookie.httponly
+ if cookie.httponly then
+ t[#t+1] = cookie.secure
+ end
+ tab[#tab+1] = 'Set-Cookie: '..concat(t, "; ")
+ end
+ return tab
+end
+
+-- 清理Cookie
+function Cookie.clear ()
+ local co = co_self()
+ Cookie.server[co] = nil
+ Cookie.client[co] = nil
+ -- local log = require 'logging'
+ -- local Log = log:new()
+ -- Log:DEBUG('反序列化后的cookie', Cookie)
+end
+
+return Cookie
diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua
index 58633bc1..ab008f16 100644
--- a/lualib/httpd/init.lua
+++ b/lualib/httpd/init.lua
@@ -97,12 +97,25 @@ function httpd:server_name(server)
end
end
+-- 连接保持时间
function httpd:timeout(timeout)
if type(timeout) == "number" then
self.__timeout = timeout
end
end
+-- 是否记录解析cookie
+function httpd:enable_cookie ()
+ self.__cookie = true
+end
+
+-- 设置Cookie加密Key
+function httpd:cookie_secure (secure)
+ if type(secure) == 'string' and secure ~= '' then
+ self.__cookie_secure = secure
+ end
+end
+
-- 注册静态文件读取路径, foldor是一个目录, ttl是静态文件缓存周期
function httpd:static(foldor, ttl)
if foldor and type(foldor) == 'string' and #foldor > 0 then
@@ -126,8 +139,10 @@ end
-- 记录日志到文件
function httpd:log(path)
- self.logpath = path or "cf-httpd.log"
+ if type(path) == 'string' and path ~= '' then
+ self.logpath = path
self.log = log:new({ path = self.logpath })
+ end
end
function httpd:tolog(code, path, ip, ip_list, method, speed)
diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua
index 9e165c83..21f5889e 100644
--- a/lualib/protocol/http.lua
+++ b/lualib/protocol/http.lua
@@ -10,6 +10,7 @@ local sha1 = crypt.sha1
local base64 = crypt.base64encode
local now = sys.now
local DATE = os.date
+local insert = table.insert
local form = require "httpd.Form"
local FILE_TYPE = form.FILE
@@ -17,6 +18,13 @@ local ARGS_TYPE = form.ARGS
local form_multipart = form.multipart
local form_urlencode = form.urlencode
+local Cookie = require "httpd.Cookie"
+local clCookie = Cookie.clear -- 清理
+local secCookie = Cookie.setSecure -- 设置Cookie加密字段
+local seCookie = Cookie.serialization -- 序列化
+local deCookie = Cookie.deserialization -- 反序列化
+
+
local Router = require "httpd.Router"
local ROUTE_FIND = Router.find
local ROUTE_REGISTERY = Router.registery
@@ -373,10 +381,13 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
local ttl = http.ttl
local server = http.__server
local timeout = http.__timeout
+ local cookie = http.__cookie
+ local cookie_secure = http.__cookie_secure
local before_func = http._before_func
local max_path_size = http.__max_path_size
local max_header_size = http.__max_header_size
local max_body_size = http.__max_body_size
+ secCookie(cookie_secure) -- 如果需要
local sock = tcp:new():set_fd(fd):timeout(timeout or 15)
while 1 do
local buf = sock:recv(1024)
@@ -499,6 +510,10 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
local ok, body, static, statucode
if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then
+ -- 如果httpd开启了记录Cookie字段, 则每次尝试是否deCookie
+ if cookie and typ == HTTP_PROTOCOL.USE then
+ deCookie(content['headers']["Cookie"])
+ end
if type(cls) == "table" then
local method = cls[lower(METHOD)]
if not method or type(method) ~= 'function' then -- 注册的路由未实现这个方法
@@ -510,6 +525,14 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
else
ok, body = pcall(cls, content)
end
+ -- 如果httpd开启了记录Cookie字段, 则每次尝试是否需要seCookie
+ if cookie and typ == HTTP_PROTOCOL.USE then
+ local Cookies = seCookie()
+ for _, Cookie in ipairs(Cookies) do
+ header[#header+1] = Cookie
+ end
+ clCookie()
+ end
if not ok then
Log:ERROR(body)
statucode = 500
@@ -517,7 +540,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
return sock:close()
end
statucode = 200
- header[#header+1] = REQUEST_STATUCODE_RESPONSE(statucode)
+ insert(header, 1, REQUEST_STATUCODE_RESPONSE(statucode))
elseif typ == HTTP_PROTOCOL.WS then
local ok, msg = pcall(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start)
if not ok then
From 0f86037837ad5af7cd11f84bfc22f06f214b470a Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 3 May 2019 01:44:09 +0800
Subject: [PATCH 023/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=93=E9=97=A8?=
=?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97=E7=9B=AE=E5=BD=95,=20=E6=89=80?=
=?UTF-8?q?=E6=9C=89=E5=86=85=E9=83=A8=E6=97=A5=E5=BF=97=E4=B8=8E=E7=94=A8?=
=?UTF-8?q?=E6=88=B7dump=E5=87=BA=E7=9A=84=E5=B0=86=E9=83=BD=E8=AE=B0?=
=?UTF-8?q?=E5=BD=95=E5=9C=A8=E8=BF=99=E9=87=8C.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
logs/README.md | 3 +++
lualib/Cache/init.lua | 2 +-
lualib/DB/init.lua | 2 +-
lualib/MQ/init.lua | 2 +-
lualib/httpd/Router.lua | 2 +-
lualib/httpd/init.lua | 5 ++---
lualib/internal/Co.lua | 2 +-
lualib/internal/TCP.lua | 2 +-
lualib/internal/Timer.lua | 2 +-
9 files changed, 12 insertions(+), 10 deletions(-)
create mode 100644 logs/README.md
diff --git a/logs/README.md b/logs/README.md
new file mode 100644
index 00000000..55dceb52
--- /dev/null
+++ b/logs/README.md
@@ -0,0 +1,3 @@
+## 日志文件夹
+
+> 日志将会自动按照日期切割
diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua
index 2192da3c..4506d272 100644
--- a/lualib/Cache/init.lua
+++ b/lualib/Cache/init.lua
@@ -17,7 +17,7 @@ local upper = string.upper
local lower = string.lower
local splite = string.gmatch
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'Cache'})
-- 注册命令
local commands = {
diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua
index 7785190a..124f943b 100644
--- a/lualib/DB/init.lua
+++ b/lualib/DB/init.lua
@@ -3,7 +3,7 @@ local timer = require "internal.Timer"
local co = require "internal.Co"
local class = require "class"
local log = require "logging"
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'DB'})
local co_self = co.self
local co_wait = co.wait
diff --git a/lualib/MQ/init.lua b/lualib/MQ/init.lua
index 9e426776..0e00c984 100644
--- a/lualib/MQ/init.lua
+++ b/lualib/MQ/init.lua
@@ -4,7 +4,7 @@ local Timer = require "internal.Timer"
local mqtt = require "protocol.mqtt"
local redis = require "protocol.redis"
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'MQ' })
local type = type
local math = math
diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua
index 1de99f2f..4c42c06c 100644
--- a/lualib/httpd/Router.lua
+++ b/lualib/httpd/Router.lua
@@ -1,5 +1,5 @@
local log = require "logging"
-local Log = log:new()
+local Log = log:new({dump = true, path = 'httpd-Router'})
local math = math
local string = string
diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua
index ab008f16..ceb1f6f6 100644
--- a/lualib/httpd/init.lua
+++ b/lualib/httpd/init.lua
@@ -140,13 +140,12 @@ end
-- 记录日志到文件
function httpd:log(path)
if type(path) == 'string' and path ~= '' then
- self.logpath = path
- self.log = log:new({ path = self.logpath })
+ self.log = log:new({ dump = true, path = path })
end
end
function httpd:tolog(code, path, ip, ip_list, method, speed)
- if self.logpath then
+ if self.log then
local log = fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)
self.log:dump(log)
end
diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua
index 191069f4..c8a9a136 100644
--- a/lualib/internal/Co.lua
+++ b/lualib/internal/Co.lua
@@ -1,7 +1,7 @@
local log = require "logging"
local task = require "task"
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'internal-Co' })
local type = type
local assert = assert
diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua
index d1e0b284..fd09f8e8 100644
--- a/lualib/internal/TCP.lua
+++ b/lualib/internal/TCP.lua
@@ -4,7 +4,7 @@ local co = require "internal.Co"
local class = require "class"
local tcp = require "tcp"
local log = require "logging"
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'internal-TCP' })
local split = string.sub
local insert = table.insert
diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua
index 9966aeb3..61a337a9 100644
--- a/lualib/internal/Timer.lua
+++ b/lualib/internal/Timer.lua
@@ -1,7 +1,7 @@
local co = require "internal.Co"
local ti = require "timer"
local log = require "logging"
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'internal-Timer' })
local type = type
local pcall = pcall
From d5558853f14efc2ec0fc6228bbb701b771a4cec1 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 3 May 2019 01:45:48 +0800
Subject: [PATCH 024/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=97=A5=E5=BF=97?=
=?UTF-8?q?=E4=BF=9D=E5=AD=98=E6=96=B9=E5=BC=8F=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/logging/init.lua | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua
index a83f8f9d..c6171c6d 100644
--- a/lualib/logging/init.lua
+++ b/lualib/logging/init.lua
@@ -1,5 +1,4 @@
-- logging 核心配置
-
local class = require "class"
local system = require "system"
local now = system.now
@@ -94,12 +93,11 @@ function FMT (where, level, ...)
return concat({ fmt_Y_m_d_H_M_S(), where, level, ':', fmt(...), '\n'}, ' ')
end
-local paths = {}
-
local Log = class("Log")
function Log:ctor (opt)
if type(opt) == 'table' then
+ self.to_dump = opt.dump
self.path = opt.path
self.now = Y_m_d()
end
@@ -129,21 +127,20 @@ function Log:WARN (...)
self:dump(FMT(debuginfo(), "[WARN]", ...))
end
+-- dump日志到磁盘
function Log:dump(log)
- local file = paths[self.path]
- if type(self.path) == 'string' and self.path ~= '' then
- if not file then
- file = io_open(self.path..'_'..self.now..'.log', 'a')
- paths[self.path] = file
+ if self.to_dump and self.path then
+ if not self.file then
+ self.file, err = io_open('logs/'..self.path..'_'..self.now..'.log', 'a')
else
- if Y_m_d() ~= self.now then
- file:close()
- self.now = Y_m_d()
- file = io_open(self.path..'_'..self.now..'.log', 'a')
- paths[self.path] = file
+ local now = Y_m_d()
+ if now ~= self.now then
+ self.file:close()
+ self.now = now
+ self.file = io_open('logs/'..self.path..'_'..self.now..'.log', 'a')
end
end
- file:write(log):flush()
+ self.file:write(log):flush()
end
end
From 68297d8a925be1fec9ac3b38de6ec9ff8fa532f2 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 3 May 2019 02:14:18 +0800
Subject: [PATCH 025/956] =?UTF-8?q?=E8=A1=A5=E5=85=85protocol=E5=86=85?=
=?UTF-8?q?=E7=9A=84logging=E8=AE=B0=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/httpd/init.lua | 13 ++++++-------
lualib/protocol/dns.lua | 3 ++-
lualib/protocol/http.lua | 2 +-
lualib/protocol/mqtt/init.lua | 2 +-
lualib/protocol/redis.lua | 2 +-
lualib/protocol/websocket/server.lua | 2 +-
6 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua
index ceb1f6f6..0f493e53 100644
--- a/lualib/httpd/init.lua
+++ b/lualib/httpd/init.lua
@@ -140,24 +140,23 @@ end
-- 记录日志到文件
function httpd:log(path)
if type(path) == 'string' and path ~= '' then
- self.log = log:new({ dump = true, path = path })
+ self.logging = log:new({ dump = true, path = path })
end
end
function httpd:tolog(code, path, ip, ip_list, method, speed)
- if self.log then
- local log = fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)
- self.log:dump(log)
+ if self.logging then
+ self.logging:dump(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed))
end
print(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed))
end
-- 监听请求
function httpd:listen(ip, port)
- return self.IO:listen(ip, port, function (fd, ipaddr)
- ipaddr = match(ipaddr, '^::[f]+:(.+)') or ipaddr
- return EVENT_DISPATCH(fd, ipaddr, self)
+ self.IO:listen(ip, port, function (fd, ipaddr)
+ return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self)
end)
+ return self
end
-- 正确的运行方式
diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua
index 7bb920cb..23b49820 100644
--- a/lualib/protocol/dns.lua
+++ b/lualib/protocol/dns.lua
@@ -2,6 +2,7 @@ local UDP = require "internal.UDP"
local co = require "internal.Co"
local sys = require "sys"
local log = require "logging"
+local Log = log:new({ dump = true, path = 'protocol-dns'})
local prefix = '::ffff:'
@@ -225,7 +226,7 @@ local function dns_query(domain)
break
end
if type(len) == "string" then
- log.info("正在解析["..domain.."]:"..len)
+ Log:INFO("正在解析["..domain.."]:"..len)
end
end
if not len or len < LIMIT_HEADER_LEN then
diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua
index 21f5889e..245cdf6c 100644
--- a/lualib/protocol/http.lua
+++ b/lualib/protocol/http.lua
@@ -3,7 +3,7 @@ local sys = require "system"
local tcp = require "internal.TCP"
local wsserver = require "protocol.websocket.server"
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'protocol-http'})
local crypt = require "crypt"
local sha1 = crypt.sha1
diff --git a/lualib/protocol/mqtt/init.lua b/lualib/protocol/mqtt/init.lua
index b9e6b777..d4e2e2de 100644
--- a/lualib/protocol/mqtt/init.lua
+++ b/lualib/protocol/mqtt/init.lua
@@ -23,7 +23,7 @@ local make_packet4 = protocol4.make_packet
local parse_packet4 = protocol4.parse_packet
local connack_return_code = protocol4.connack_return_code
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'protocol-MQTT'})
-- cache to locals
local type = type
diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua
index 1b2310cf..17d2710f 100644
--- a/lualib/protocol/redis.lua
+++ b/lualib/protocol/redis.lua
@@ -4,7 +4,7 @@ local Co = require "internal.Co"
local tcp = require "internal.TCP"
local concat = table.concat
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'protocol-redis' })
local co_spwan = Co.spwan
diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua
index b0098e45..1a54f5dc 100644
--- a/lualib/protocol/websocket/server.lua
+++ b/lualib/protocol/websocket/server.lua
@@ -5,7 +5,7 @@ local wbproto = require "protocol.websocket.protocol"
local _recv_frame = wbproto.recv_frame
local _send_frame = wbproto.send_frame
-local Log = log:new()
+local Log = log:new({ dump = true, path = 'protocol-websocket-server'})
local co_self = co.self
local co_wait = co.wait
From bee9359bad1c51c883c0cb2473c5c40f67c420c7 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 3 May 2019 02:25:41 +0800
Subject: [PATCH 026/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=96=87=E4=BB=B6?=
=?UTF-8?q?=E5=A4=B9=E4=B8=8E=E6=90=9C=E7=B4=A2=E6=96=87=E4=BB=B6=E8=B7=AF?=
=?UTF-8?q?=E5=BE=84=E4=BC=98=E5=85=88=E7=BA=A7=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/core.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/core.c b/src/core.c
index c3b63398..89211fa8 100644
--- a/src/core.c
+++ b/src/core.c
@@ -142,7 +142,7 @@ static void *
L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){
// 为lua内存hook注入日志;
/* 用户自定义数据 */
- (void)ud; (void)osize;
+ (void)ud; (void)osize;
if (nsize == 0) return xfree(ptr), NULL;
for (;;) {
void *newptr = xrealloc(ptr, nsize);
@@ -170,10 +170,10 @@ init_lua_libs(lua_State *L){
/* 注入lua搜索域 */
lua_getglobal(L, "package");
- lua_pushliteral(L, "./script/?.lua;./script/?/init.lua;./lualib/?.lua;./lualib/?/init.lua;./?/init.lua");
+ lua_pushliteral(L, "lualib/?.lua;lualib/?/init.lua;script/?.lua;script/?/init.lua;");
lua_setfield(L, 1, "path");
- lua_pushliteral(L, "./luaclib/?.so;./luaclib/?/init.so;");
+ lua_pushliteral(L, "luaclib/?.so;luaclib/?/init.so;");
lua_setfield(L, 1, "cpath");
lua_settop(L, 0);
From e08003426c82206e75212addfc31bfece41fea50 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 3 May 2019 08:26:37 +0800
Subject: [PATCH 027/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=B7=AF=E7=94=B1?=
=?UTF-8?q?=E6=9F=A5=E6=89=BE=E7=AE=97=E6=B3=95,=20=E9=80=82=E5=BA=94?=
=?UTF-8?q?=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6=E6=90=9C=E7=B4=A2=E8=B7=AF?=
=?UTF-8?q?=E5=BE=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/httpd/Router.lua | 37 +++++++++++++++++++++++++++++++++----
lualib/httpd/init.lua | 40 ++++++++++++++++------------------------
lualib/protocol/http.lua | 4 ++--
3 files changed, 51 insertions(+), 30 deletions(-)
diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua
index 4c42c06c..579eb234 100644
--- a/lualib/httpd/Router.lua
+++ b/lualib/httpd/Router.lua
@@ -3,6 +3,7 @@ local Log = log:new({dump = true, path = 'httpd-Router'})
local math = math
local string = string
+local split = string.sub
local find = string.find
local match = string.match
local splite = string.gmatch
@@ -13,6 +14,9 @@ local ipairs = ipairs
local tonumber = tonumber
local tostring = tostring
local toint = math.tointeger
+local io_open = io.open
+
+local load_file
local Router = {
API = 1,
@@ -23,6 +27,8 @@ local Router = {
local routes = {} -- 存储路由
+local static = {} -- 静态文件路由
+
local typ = {
int = toint,
float = tonumber,
@@ -38,6 +44,16 @@ local function splite_route(route)
return tab
end
+local function registery_static (prefix, route_type)
+ if route_type == Router.STATIC then
+ if not next(static) then
+ static.prefix = prefix
+ static.type = route_type
+ end
+ return
+ end
+end
+
local function registery_router(route, class, route_type)
local tab = splite_route(route)
if route == '/' then -- 如果注册路由为'/', 则转义为:''
@@ -64,18 +80,26 @@ local function find_route(path)
for index, route in ipairs(routes) do
local r = tab[index]
if not r then
- return false
+ break
end
local t = route[r]
if type(t) == 'table' then
if #tab == index then
return t.class, t.type
end
- if t.type == Router.STATIC then
- return t.class, t.type
- end
end
end
+ local prefix, type = static.prefix, static.type
+ load_file = load_file or function (path)
+ local f, error = io_open(prefix..path, 'rb')
+ if not f then
+ return
+ end
+ local file = f:read('*a')
+ f:close()
+ return file, match(path, '.+%.([%a]+)')
+ end
+ return load_file, type
end
-- 查找路由
@@ -83,6 +107,11 @@ function Router.find(path)
return find_route(path)
end
+-- 注册静态文件查找路径
+function Router.static (...)
+ return registery_static(...)
+end
+
-- 注册路由
function Router.registery(route, class, route_type)
if type(route) ~= 'string' or route == '' then -- 过滤错误的路由输入
diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua
index 0f493e53..0e462539 100644
--- a/lualib/httpd/init.lua
+++ b/lualib/httpd/init.lua
@@ -1,4 +1,5 @@
local HTTP = require "protocol.http"
+local HTTPD = require "httpd.Router"
local tcp = require "internal.TCP"
local class = require "class"
local sys = require "system"
@@ -16,36 +17,37 @@ local os_date = os.date
-- 请求解析
local EVENT_DISPATCH = HTTP.EVENT_DISPATCH
--- 注册HTTP路由
-local HTTP_ROUTE_REGISTERY = HTTP.ROUTE_REGISTERY
+-- 注册HTTP路由与静态文件
+local HTTP_STATIC_REGISTERY = HTTPD.static
+local HTTP_ROUTE_REGISTERY = HTTPD.registery
+
local httpd = class("httpd")
function httpd:ctor(opt)
- self.API = HTTP.API
- self.USE = HTTP.USE
+ self.API = HTTPD.API
+ self.USE = HTTPD.USE
self.IO = tcp:new()
end
-- 用来注册WebSocket对象
function httpd:ws(route, class)
if route and type(class) == "table" then
- -- HTTP_ROUTE_REGISTERY(self.routes, route, class, HTTP.WS)
- HTTP_ROUTE_REGISTERY(route, class, HTTP.WS)
+ HTTP_ROUTE_REGISTERY(route, class, HTTPD.WS)
end
end
-- 用来注册接口
function httpd:api(route, class)
if route and (type(class) == "table" or type(class) == "function")then
- HTTP_ROUTE_REGISTERY(route, class, HTTP.API)
+ HTTP_ROUTE_REGISTERY(route, class, HTTPD.API)
end
end
-- 用来注册普通路由
function httpd:use(route, class)
if route and (type(class) == "table" or type(class) == "function") then
- HTTP_ROUTE_REGISTERY(route, class, HTTP.USE)
+ HTTP_ROUTE_REGISTERY(route, class, HTTPD.USE)
end
end
@@ -118,23 +120,13 @@ end
-- 注册静态文件读取路径, foldor是一个目录, ttl是静态文件缓存周期
function httpd:static(foldor, ttl)
- if foldor and type(foldor) == 'string' and #foldor > 0 then
- ttl = math.tointeger(ttl)
- if ttl and ttl > 0 then
- self.ttl = ttl
- end
- HTTP_ROUTE_REGISTERY('./'..foldor, function (path)
- if path then
- local FILE = io_open(path, "rb")
- if not FILE then
- return
- end
- local file = FILE:read('*a')
- FILE:close()
- return file, match(path, '.+%.([%a]+)')
- end
- end, HTTP.STATIC)
+ if not self.foldor then
+ self.foldor = foldor or 'static'
+ if ttl and ttl > 0 then
+ self.ttl = ttl
end
+ return HTTP_STATIC_REGISTERY(self.foldor, HTTPD.STATIC)
+ end
end
-- 记录日志到文件
diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua
index 245cdf6c..b1851b03 100644
--- a/lualib/protocol/http.lua
+++ b/lualib/protocol/http.lua
@@ -553,9 +553,9 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
local path = PATH
local pos, _ = find(PATH, '%?')
if pos then
- path = split(path, 1, pos - 1)
+ path = split(PATH, 1, pos - 1)
end
- ok, body, file_type = pcall(cls, './'..path)
+ ok, body, file_type = pcall(cls, path)
if not ok then
Log:ERROR(body)
statucode = 500
From 5732b3948ae924394e9f596a877e1ac2410398e4 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 3 May 2019 11:03:53 +0800
Subject: [PATCH 028/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DCookie=E7=9A=84?=
=?UTF-8?q?=E4=B8=80=E4=BA=9B=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/httpd/Cookie.lua | 17 +++++++++++------
lualib/protocol/http.lua | 2 +-
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua
index 84405982..5eec6a06 100644
--- a/lualib/httpd/Cookie.lua
+++ b/lualib/httpd/Cookie.lua
@@ -60,24 +60,29 @@ function Cookie.setCookie (name, value, expires, notall, https)
}
end
--- 获取置顶Cookie字段
+-- 获取指定Cookie字段
function Cookie.getCookie (name)
- local co = co_self
+ local co = co_self()
local cs = Cookie.client[co]
- local value = cs.client[name]
+ local value = cs[name]
if not value then
return
end
return decode_value(value)
end
+function Cookie.delCookie (name)
+ return Cookie.setCookie(name, ' ', os_time() + 1)
+end
+
-- 对cookie进行反序列化
function Cookie.deserialization (cs)
+ local co = co_self()
+ local Cookies = {}
if type(cs) ~= 'string' or cs == '' then
+ Cookie.client[co] = Cookies
return
end
- local co = co_self()
- local Cookies = {}
for name, value in splite(cs, '([^ ;]+)=([^ ;]+)') do
Cookies[name] = value
end
@@ -110,7 +115,7 @@ function Cookie.serialization ()
end
-- 清理Cookie
-function Cookie.clear ()
+function Cookie.clean ()
local co = co_self()
Cookie.server[co] = nil
Cookie.client[co] = nil
diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua
index b1851b03..b6a73d2c 100644
--- a/lualib/protocol/http.lua
+++ b/lualib/protocol/http.lua
@@ -19,7 +19,7 @@ local form_multipart = form.multipart
local form_urlencode = form.urlencode
local Cookie = require "httpd.Cookie"
-local clCookie = Cookie.clear -- 清理
+local clCookie = Cookie.clean -- 清理
local secCookie = Cookie.setSecure -- 设置Cookie加密字段
local seCookie = Cookie.serialization -- 序列化
local deCookie = Cookie.deserialization -- 反序列化
From 7fa4a34f1b8feb5edda5e62810dc93c8a2e2a79e Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 3 May 2019 13:51:05 +0800
Subject: [PATCH 029/956] =?UTF-8?q?=E5=B0=86=E8=B7=AF=E7=94=B1=E6=9F=A5?=
=?UTF-8?q?=E6=89=BE=E8=B0=83=E6=95=B4=E4=B8=BAhash=20key-value=E6=98=A0?=
=?UTF-8?q?=E5=B0=84=E6=9F=A5=E6=89=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/httpd/Router.lua | 65 +++++++++++++----------------------------
1 file changed, 21 insertions(+), 44 deletions(-)
diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua
index 579eb234..09520032 100644
--- a/lualib/httpd/Router.lua
+++ b/lualib/httpd/Router.lua
@@ -1,6 +1,9 @@
local log = require "logging"
local Log = log:new({dump = true, path = 'httpd-Router'})
+local crypt = require "crypt"
+local hexencode = crypt.hexencode
+
local math = math
local string = string
local split = string.sub
@@ -13,10 +16,9 @@ local assert = assert
local ipairs = ipairs
local tonumber = tonumber
local tostring = tostring
-local toint = math.tointeger
local io_open = io.open
-
-local load_file
+local concat = table.concat
+local toint = math.tointeger
local Router = {
API = 1,
@@ -35,59 +37,34 @@ local typ = {
string = tostring,
}
--- 路由分割: /a/b/c/d = {'a', 'b', 'c', 'd'}
-local function splite_route(route)
+-- 分割路径后进行hex, 得到key后一次查表即可完成
+local function hex_route(route)
local tab = {}
for r in splite(route, '/([^/?]+)') do
tab[#tab + 1] = r
end
- return tab
+ return hexencode(concat(tab))
end
local function registery_static (prefix, route_type)
- if route_type == Router.STATIC then
- if not next(static) then
- static.prefix = prefix
- static.type = route_type
- end
- return
+ if not next(static) then
+ static.prefix = prefix
+ static.type = route_type
end
+ return
end
-local function registery_router(route, class, route_type)
- local tab = splite_route(route)
- if route == '/' then -- 如果注册路由为'/', 则转义为:''
- tab = {''}
- end
- for index, r in ipairs(tab) do
- if not routes[index] then
- routes[index] = {}
- end
- if not routes[index][r] then
- routes[index][r] = true
- end
- if #tab == index then
- routes[index][r] = {class = class, type = route_type}
- end
- end
+local load_file
+
+local function registery_router (route, class, route_type)
+ routes[hex_route(route)] = {class = class, type = route_type}
end
-local function find_route(path)
- local tab = splite_route(path)
- if #tab == 0 then -- 如果路由为/[/]{0, n}, 则转义为: ''
- tab[1] = ''
- end
- for index, route in ipairs(routes) do
- local r = tab[index]
- if not r then
- break
- end
- local t = route[r]
- if type(t) == 'table' then
- if #tab == index then
- return t.class, t.type
- end
- end
+local function find_route (path)
+ local hex = hex_route(path)
+ local t = routes[hex]
+ if t then
+ return t.class, t.type
end
local prefix, type = static.prefix, static.type
load_file = load_file or function (path)
From 9cffd9f58d9352dfe7023e532c8983fb7744e391 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Sat, 4 May 2019 15:54:42 +0800
Subject: [PATCH 030/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9D=99=E6=80=81?=
=?UTF-8?q?=E6=96=87=E4=BB=B6=E8=B7=A8=E5=9F=9F=E9=BB=98=E8=AE=A4=E6=94=AF?=
=?UTF-8?q?=E6=8C=81i?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/protocol/http.lua | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua
index b6a73d2c..261230d7 100644
--- a/lualib/protocol/http.lua
+++ b/lualib/protocol/http.lua
@@ -576,6 +576,8 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
else
static = fmt('Content-Type: %s', conten_type)
end
+ -- 如果是静态文件, 增加默认跨域访问支持
+ header[#header+1] = "Access-Control-Allow-Origin: *"
end
header[#header+1] = HTTP_DATE()
header[#header+1] = 'Origin: *'
From 0d57484debb6bc1acf109469a1adcb87ab4ae3c4 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Sun, 5 May 2019 02:31:15 +0800
Subject: [PATCH 031/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9C=AA=E6=B3=A8?=
=?UTF-8?q?=E5=86=8Chttpd=20static=E4=B9=9F=E6=9F=A5=E6=89=BE=E8=B7=AF?=
=?UTF-8?q?=E7=94=B1=E7=9A=84bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/httpd/Router.lua | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua
index 09520032..99d7ed9e 100644
--- a/lualib/httpd/Router.lua
+++ b/lualib/httpd/Router.lua
@@ -67,6 +67,9 @@ local function find_route (path)
return t.class, t.type
end
local prefix, type = static.prefix, static.type
+ if not prefix and not type then
+ return
+ end
load_file = load_file or function (path)
local f, error = io_open(prefix..path, 'rb')
if not f then
From 3542e31022f4eff9f66159518da8c9b8f9f9e3e7 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Tue, 7 May 2019 14:20:48 +0800
Subject: [PATCH 032/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0lua=E7=9A=84urlencode?=
=?UTF-8?q?=E4=B8=8Eurldecode=E5=BA=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/url/init.lua | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 lualib/url/init.lua
diff --git a/lualib/url/init.lua b/lualib/url/init.lua
new file mode 100644
index 00000000..d5d42c9f
--- /dev/null
+++ b/lualib/url/init.lua
@@ -0,0 +1,19 @@
+local tonumber = tonumber
+local byte = string.byte
+local char = string.char
+local fmt = string.format
+local spliter = string.gsub
+
+local url = {}
+
+-- urlencode编码
+function url.encode(s)
+ return spliter(spliter(s, "([^%w%.%- ])", function(c) return fmt("%%%02X", byte(c)) end), " ", "+")
+end
+
+-- urlencode解码
+function url.decode(s)
+ return spliter(s, '%%(%x%x)', function(h) return char(tonumber(h, 16)) end)
+end
+
+return url
From 0b4e2901a9faeed20c158040d690e5a9168bd453 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 10 May 2019 14:28:07 +0800
Subject: [PATCH 033/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?=
=?UTF-8?q?=E5=88=A4=E6=96=AD=E8=81=94=E5=90=88=E6=9F=A5=E8=AF=A2=E4=B8=8D?=
=?UTF-8?q?=E9=9C=80=E8=A6=81format?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/DB/init.lua | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua
index 124f943b..8be58a75 100644
--- a/lualib/DB/init.lua
+++ b/lualib/DB/init.lua
@@ -118,7 +118,7 @@ end
-- 将['field', '=', 'a'] 格式化为 field = a
-- 将['field', 'NOT', 'IN', '(1, 2, 3)'] 格式化为 field NOT IN (1, 2, 3)
local function format(t)
- return fmt(concat({rep("%s ", #t-1), "%s"}), unpack(t))
+ return fmt(rep("'%s' ", #t), unpack(t))
end
local function format_value1(t)
@@ -220,7 +220,7 @@ local function where(query, conditions)
insert(CONDITIONS, format({condition[1], con2, LEFT..format_value2(condition[3], c)..RIGHT}))
elseif con2 == IS then
insert(CONDITIONS, concat(condition, " "))
- elseif find(condition[3], condition[1]) then
+ elseif find(condition[3], condition[1]) or find(condition[3], '`.') then
insert(CONDITIONS, concat(condition, " "))
else
insert(CONDITIONS, format_value3(condition))
@@ -444,6 +444,7 @@ function DB:query(query)
return nil, "DB尚未初始化"
end
assert(type(query) == 'string' and query ~= '' , "原始SQL类型错误(query):"..tostring(query))
+ -- Log:DEBUG(query)
local db, ret, err
while 1 do
db = pop_db(self)
From cf31bfe3b5aac1c2a19dd1941f347ec5a84e0c2d Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Fri, 10 May 2019 23:07:07 +0800
Subject: [PATCH 034/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAHTT?=
=?UTF-8?q?P=E5=A4=B4=E9=83=A8=E7=9A=84=E9=97=AE=E9=A2=98,=20=E4=B8=BA?=
=?UTF-8?q?=E6=89=80=E6=9C=89=E6=83=85=E5=86=B5=E5=A2=9E=E5=8A=A0body?=
=?UTF-8?q?=E9=95=BF=E5=BA=A6=E6=8F=90=E5=8D=87=E5=85=BC=E5=AE=B9=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/protocol/http.lua | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua
index 261230d7..8652f9eb 100644
--- a/lualib/protocol/http.lua
+++ b/lualib/protocol/http.lua
@@ -590,15 +590,15 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http)
header[#header+1] = Connection
if body then
if type(body) == 'string' then
- if #body >= 1 then
- header[#header+1] = 'Transfer-Encoding: identity'
- header[#header+1] = 'Content-Length: '.. #body
- end
+ header[#header+1] = 'Transfer-Encoding: identity'
+ header[#header+1] = 'Content-Length: '.. #body
else
Log:WARN('response body not a string type.'..'('..tostring(body)..')')
+ header[#header+1] = 'Content-Length: 0'
body = ''
end
else
+ header[#header+1] = 'Content-Length: 0'
body = ''
end
if typ == HTTP_PROTOCOL.API then
From a9ca946ec127a557695f19f8a6422cc03e60c6e9 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Tue, 14 May 2019 00:40:16 +0800
Subject: [PATCH 035/956] =?UTF-8?q?=E4=B8=BAcrypt=E5=A2=9E=E5=8A=A0crc-32?=
=?UTF-8?q?=E4=B8=8Ecrc-64=E6=96=B9=E6=B3=95,=20=E5=BA=94=E7=94=A8?=
=?UTF-8?q?=E4=B8=80=E4=BA=9B=E7=89=B9=E6=AE=8A=E5=9C=BA=E6=99=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
luaclib/src/lcrypt.c | 228 ++++++++++++++++++++++++++++++++++++++++++
lualib/crypt/init.lua | 10 ++
2 files changed, 238 insertions(+)
diff --git a/luaclib/src/lcrypt.c b/luaclib/src/lcrypt.c
index 11085430..6e2a8bcb 100644
--- a/luaclib/src/lcrypt.c
+++ b/luaclib/src/lcrypt.c
@@ -43,6 +43,205 @@ static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+/* CRC32 TAB */
+static uint32_t CRC32[] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL,
+ 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L,
+ 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L,
+ 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L,
+ 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,
+ 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL,
+ 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L,
+ 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L,
+ 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L,
+ 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,
+ 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L,
+ 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL,
+ 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL,
+ 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL,
+ 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,
+ 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L,
+ 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL,
+ 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L,
+ 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL,
+ 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,
+ 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL,
+ 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L,
+ 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L,
+ 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L,
+ 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L,
+ 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL,
+ 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL,
+ 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L,
+ 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,
+ 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL,
+ 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L,
+ 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL,
+ 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L,
+ 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,
+ 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L,
+ 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L,
+ 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL,
+ 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L,
+ 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,
+ 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL,
+ 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L,
+ 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L,
+ 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL,
+ 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,
+ 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L,
+ 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L,
+ 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL,
+ 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L,
+ 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L,
+ 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL,
+ 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L,
+ 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
+};
+
+/* CRC64 TAB */
+static uint64_t CRC64[] = {
+ 0x0000000000000000, 0x7ad870c830358979,
+ 0xf5b0e190606b12f2, 0x8f689158505e9b8b,
+ 0xc038e5739841b68f, 0xbae095bba8743ff6,
+ 0x358804e3f82aa47d, 0x4f50742bc81f2d04,
+ 0xab28ecb46814fe75, 0xd1f09c7c5821770c,
+ 0x5e980d24087fec87, 0x24407dec384a65fe,
+ 0x6b1009c7f05548fa, 0x11c8790fc060c183,
+ 0x9ea0e857903e5a08, 0xe478989fa00bd371,
+ 0x7d08ff3b88be6f81, 0x07d08ff3b88be6f8,
+ 0x88b81eabe8d57d73, 0xf2606e63d8e0f40a,
+ 0xbd301a4810ffd90e, 0xc7e86a8020ca5077,
+ 0x4880fbd87094cbfc, 0x32588b1040a14285,
+ 0xd620138fe0aa91f4, 0xacf86347d09f188d,
+ 0x2390f21f80c18306, 0x594882d7b0f40a7f,
+ 0x1618f6fc78eb277b, 0x6cc0863448deae02,
+ 0xe3a8176c18803589, 0x997067a428b5bcf0,
+ 0xfa11fe77117cdf02, 0x80c98ebf2149567b,
+ 0x0fa11fe77117cdf0, 0x75796f2f41224489,
+ 0x3a291b04893d698d, 0x40f16bccb908e0f4,
+ 0xcf99fa94e9567b7f, 0xb5418a5cd963f206,
+ 0x513912c379682177, 0x2be1620b495da80e,
+ 0xa489f35319033385, 0xde51839b2936bafc,
+ 0x9101f7b0e12997f8, 0xebd98778d11c1e81,
+ 0x64b116208142850a, 0x1e6966e8b1770c73,
+ 0x8719014c99c2b083, 0xfdc17184a9f739fa,
+ 0x72a9e0dcf9a9a271, 0x08719014c99c2b08,
+ 0x4721e43f0183060c, 0x3df994f731b68f75,
+ 0xb29105af61e814fe, 0xc849756751dd9d87,
+ 0x2c31edf8f1d64ef6, 0x56e99d30c1e3c78f,
+ 0xd9810c6891bd5c04, 0xa3597ca0a188d57d,
+ 0xec09088b6997f879, 0x96d1784359a27100,
+ 0x19b9e91b09fcea8b, 0x636199d339c963f2,
+ 0xdf7adabd7a6e2d6f, 0xa5a2aa754a5ba416,
+ 0x2aca3b2d1a053f9d, 0x50124be52a30b6e4,
+ 0x1f423fcee22f9be0, 0x659a4f06d21a1299,
+ 0xeaf2de5e82448912, 0x902aae96b271006b,
+ 0x74523609127ad31a, 0x0e8a46c1224f5a63,
+ 0x81e2d7997211c1e8, 0xfb3aa75142244891,
+ 0xb46ad37a8a3b6595, 0xceb2a3b2ba0eecec,
+ 0x41da32eaea507767, 0x3b024222da65fe1e,
+ 0xa2722586f2d042ee, 0xd8aa554ec2e5cb97,
+ 0x57c2c41692bb501c, 0x2d1ab4dea28ed965,
+ 0x624ac0f56a91f461, 0x1892b03d5aa47d18,
+ 0x97fa21650afae693, 0xed2251ad3acf6fea,
+ 0x095ac9329ac4bc9b, 0x7382b9faaaf135e2,
+ 0xfcea28a2faafae69, 0x8632586aca9a2710,
+ 0xc9622c4102850a14, 0xb3ba5c8932b0836d,
+ 0x3cd2cdd162ee18e6, 0x460abd1952db919f,
+ 0x256b24ca6b12f26d, 0x5fb354025b277b14,
+ 0xd0dbc55a0b79e09f, 0xaa03b5923b4c69e6,
+ 0xe553c1b9f35344e2, 0x9f8bb171c366cd9b,
+ 0x10e3202993385610, 0x6a3b50e1a30ddf69,
+ 0x8e43c87e03060c18, 0xf49bb8b633338561,
+ 0x7bf329ee636d1eea, 0x012b592653589793,
+ 0x4e7b2d0d9b47ba97, 0x34a35dc5ab7233ee,
+ 0xbbcbcc9dfb2ca865, 0xc113bc55cb19211c,
+ 0x5863dbf1e3ac9dec, 0x22bbab39d3991495,
+ 0xadd33a6183c78f1e, 0xd70b4aa9b3f20667,
+ 0x985b3e827bed2b63, 0xe2834e4a4bd8a21a,
+ 0x6debdf121b863991, 0x1733afda2bb3b0e8,
+ 0xf34b37458bb86399, 0x8993478dbb8deae0,
+ 0x06fbd6d5ebd3716b, 0x7c23a61ddbe6f812,
+ 0x3373d23613f9d516, 0x49aba2fe23cc5c6f,
+ 0xc6c333a67392c7e4, 0xbc1b436e43a74e9d,
+ 0x95ac9329ac4bc9b5, 0xef74e3e19c7e40cc,
+ 0x601c72b9cc20db47, 0x1ac40271fc15523e,
+ 0x5594765a340a7f3a, 0x2f4c0692043ff643,
+ 0xa02497ca54616dc8, 0xdafce7026454e4b1,
+ 0x3e847f9dc45f37c0, 0x445c0f55f46abeb9,
+ 0xcb349e0da4342532, 0xb1eceec59401ac4b,
+ 0xfebc9aee5c1e814f, 0x8464ea266c2b0836,
+ 0x0b0c7b7e3c7593bd, 0x71d40bb60c401ac4,
+ 0xe8a46c1224f5a634, 0x927c1cda14c02f4d,
+ 0x1d148d82449eb4c6, 0x67ccfd4a74ab3dbf,
+ 0x289c8961bcb410bb, 0x5244f9a98c8199c2,
+ 0xdd2c68f1dcdf0249, 0xa7f41839ecea8b30,
+ 0x438c80a64ce15841, 0x3954f06e7cd4d138,
+ 0xb63c61362c8a4ab3, 0xcce411fe1cbfc3ca,
+ 0x83b465d5d4a0eece, 0xf96c151de49567b7,
+ 0x76048445b4cbfc3c, 0x0cdcf48d84fe7545,
+ 0x6fbd6d5ebd3716b7, 0x15651d968d029fce,
+ 0x9a0d8ccedd5c0445, 0xe0d5fc06ed698d3c,
+ 0xaf85882d2576a038, 0xd55df8e515432941,
+ 0x5a3569bd451db2ca, 0x20ed197575283bb3,
+ 0xc49581ead523e8c2, 0xbe4df122e51661bb,
+ 0x3125607ab548fa30, 0x4bfd10b2857d7349,
+ 0x04ad64994d625e4d, 0x7e7514517d57d734,
+ 0xf11d85092d094cbf, 0x8bc5f5c11d3cc5c6,
+ 0x12b5926535897936, 0x686de2ad05bcf04f,
+ 0xe70573f555e26bc4, 0x9ddd033d65d7e2bd,
+ 0xd28d7716adc8cfb9, 0xa85507de9dfd46c0,
+ 0x273d9686cda3dd4b, 0x5de5e64efd965432,
+ 0xb99d7ed15d9d8743, 0xc3450e196da80e3a,
+ 0x4c2d9f413df695b1, 0x36f5ef890dc31cc8,
+ 0x79a59ba2c5dc31cc, 0x037deb6af5e9b8b5,
+ 0x8c157a32a5b7233e, 0xf6cd0afa9582aa47,
+ 0x4ad64994d625e4da, 0x300e395ce6106da3,
+ 0xbf66a804b64ef628, 0xc5bed8cc867b7f51,
+ 0x8aeeace74e645255, 0xf036dc2f7e51db2c,
+ 0x7f5e4d772e0f40a7, 0x05863dbf1e3ac9de,
+ 0xe1fea520be311aaf, 0x9b26d5e88e0493d6,
+ 0x144e44b0de5a085d, 0x6e963478ee6f8124,
+ 0x21c640532670ac20, 0x5b1e309b16452559,
+ 0xd476a1c3461bbed2, 0xaeaed10b762e37ab,
+ 0x37deb6af5e9b8b5b, 0x4d06c6676eae0222,
+ 0xc26e573f3ef099a9, 0xb8b627f70ec510d0,
+ 0xf7e653dcc6da3dd4, 0x8d3e2314f6efb4ad,
+ 0x0256b24ca6b12f26, 0x788ec2849684a65f,
+ 0x9cf65a1b368f752e, 0xe62e2ad306bafc57,
+ 0x6946bb8b56e467dc, 0x139ecb4366d1eea5,
+ 0x5ccebf68aecec3a1, 0x2616cfa09efb4ad8,
+ 0xa97e5ef8cea5d153, 0xd3a62e30fe90582a,
+ 0xb0c7b7e3c7593bd8, 0xca1fc72bf76cb2a1,
+ 0x45775673a732292a, 0x3faf26bb9707a053,
+ 0x70ff52905f188d57, 0x0a2722586f2d042e,
+ 0x854fb3003f739fa5, 0xff97c3c80f4616dc,
+ 0x1bef5b57af4dc5ad, 0x61372b9f9f784cd4,
+ 0xee5fbac7cf26d75f, 0x9487ca0fff135e26,
+ 0xdbd7be24370c7322, 0xa10fceec0739fa5b,
+ 0x2e675fb4576761d0, 0x54bf2f7c6752e8a9,
+ 0xcdcf48d84fe75459, 0xb71738107fd2dd20,
+ 0x387fa9482f8c46ab, 0x42a7d9801fb9cfd2,
+ 0x0df7adabd7a6e2d6, 0x772fdd63e7936baf,
+ 0xf8474c3bb7cdf024, 0x829f3cf387f8795d,
+ 0x66e7a46c27f3aa2c, 0x1c3fd4a417c62355,
+ 0x935745fc4798b8de, 0xe98f353477ad31a7,
+ 0xa6df411fbfb21ca3, 0xdc0731d78f8795da,
+ 0x536fa08fdfd90e51, 0x29b7d047efec8728,
+};
/* the eight DES S-boxes */
@@ -1175,6 +1374,33 @@ lxor_str(lua_State *L) {
return 1;
}
+static int
+lcrc32(lua_State *L){
+ size_t len;
+ const char *str = luaL_checklstring(L, 1, &len);
+ if (len <= 0) return luaL_error(L, "#1 need a string");
+
+ uint32_t i = 0;
+ uint32_t crc = 0xFFFFFFFF;
+
+ for (i = 0; i < len; i++) crc = CRC32[ (crc ^ str[i]) & 0xff ] ^ (crc >> 8);
+ lua_pushinteger(L, crc ^ 0xFFFFFFFF);
+ return 1;
+};
+
+static int
+lcrc64(lua_State *L){
+ size_t len;
+ const char *str = luaL_checklstring(L, 1, &len);
+ if (len <= 0) return luaL_error(L, "#1 need a string");
+
+ uint32_t i = 0;
+ uint64_t crc = 0x0;
+ for (i = 0; i < len; i++) crc = CRC64[(uint8_t)crc ^ (uint8_t)str[i]] ^ (crc >> 8);
+ lua_pushnumber(L, crc);
+ return 1;
+};
+
LUAMOD_API int
luaopen_lcrypt(lua_State *L) {
luaL_checkversion(L);
@@ -1201,6 +1427,8 @@ luaopen_lcrypt(lua_State *L) {
{ "hmac_sha1", lhmac_sha1 },
{ "hmac_hash", lhmac_hash },
{ "xor_str", lxor_str },
+ { "crc32", lcrc32 },
+ { "crc64", lcrc64 },
{ NULL, NULL },
};
luaL_newlib(L, crypt_libs);
diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua
index 315f0b5f..deb267e6 100644
--- a/lualib/crypt/init.lua
+++ b/lualib/crypt/init.lua
@@ -2,6 +2,8 @@ local CRYPT = require "lcrypt"
local sha1 = CRYPT.sha1
local xor_str = CRYPT.xor_str
+local crc32 = CRYPT.crc32
+local crc64 = CRYPT.crc64
local randomkey = CRYPT.randomkey
local hashkey = CRYPT.hashkey
@@ -91,4 +93,12 @@ function crypt.dhexchange (...)
return dhexchange(...)
end
+function crypt.crc32 (...)
+ return crc32(...)
+end
+
+function crypt.crc64 (...)
+ return crc64(...)
+end
+
return crypt
From 37b14da6467bf04f2d31a4b975c5f158170ad690 Mon Sep 17 00:00:00 2001
From: CandyMi <869646063@qq.com>
Date: Tue, 14 May 2019 11:22:07 +0800
Subject: [PATCH 036/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0DB=E5=AD=97=E7=AC=A6?=
=?UTF-8?q?=E9=9B=86=E8=AE=BE=E7=BD=AE,=20=E8=A7=A3=E5=86=B3DB(mysql)?=
=?UTF-8?q?=E6=8F=92=E5=85=A5=E6=95=B0=E6=8D=AE=E4=B8=BA=E4=B9=B1=E7=A0=81?=
=?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lualib/DB/init.lua | 1 +
lualib/protocol/mysql.lua | 50 +++++++++++++++++++++++++++++++++++++--
2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua
index 8be58a75..c92e9cb4 100644
--- a/lualib/DB/init.lua
+++ b/lualib/DB/init.lua
@@ -333,6 +333,7 @@ function DB:ctor(opt)
self.username = opt.username
self.password = opt.password
self.database = opt.database
+ self.charset = opt.charset or 'utf8'
self.max = opt.max or 50
self.current = 0
-- 协程池
diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua
index 507a4027..5c5af623 100644
--- a/lualib/protocol/mysql.lua
+++ b/lualib/protocol/mysql.lua
@@ -15,6 +15,51 @@ local error = error
local tonumber = tonumber
local new_tab = function (narr, nrec) return {} end
+local CHARSET_MAP = {
+ _default = 0,
+ big5 = 1,
+ dec8 = 3,
+ cp850 = 4,
+ hp8 = 6,
+ koi8r = 7,
+ latin1 = 8,
+ latin2 = 9,
+ swe7 = 10,
+ ascii = 11,
+ ujis = 12,
+ sjis = 13,
+ hebrew = 16,
+ tis620 = 18,
+ euckr = 19,
+ koi8u = 22,
+ gb2312 = 24,
+ greek = 25,
+ cp1250 = 26,
+ gbk = 28,
+ latin5 = 30,
+ armscii8 = 32,
+ utf8 = 33,
+ ucs2 = 35,
+ cp866 = 36,
+ keybcs2 = 37,
+ macce = 38,
+ macroman = 39,
+ cp852 = 40,
+ latin7 = 41,
+ utf8mb4 = 45,
+ cp1251 = 51,
+ utf16 = 54,
+ utf16le = 56,
+ cp1256 = 57,
+ cp1257 = 59,
+ utf32 = 60,
+ binary = 63,
+ geostd8 = 92,
+ cp932 = 95,
+ eucjpms = 97,
+ gb18030 = 248
+}
+
local MySQL = class("MySQL")
local STATE_CONNECTED = 1
@@ -468,10 +513,11 @@ function MySQL.connect(self, opts)
local client_flags = 260047;
- local req = strpack("
Date: Wed, 15 May 2019 02:40:41 +0800
Subject: [PATCH 037/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=BA=9B?=
=?UTF-8?q?=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
static/css/font.css | 16 +
static/css/login.css | 105 ++++
static/css/theme1.css | 21 +
static/css/theme2.css | 21 +
static/css/theme3.css | 22 +
static/css/theme4.css | 21 +
static/css/theme5.css | 27 +
static/css/xadmin.css | 530 ++++++++++++++++
static/favicon.ico | Bin 0 -> 16958 bytes
static/fonts/iconfont.eot | Bin 0 -> 49600 bytes
static/fonts/iconfont.svg | 477 ++++++++++++++
static/fonts/iconfont.ttf | Bin 0 -> 49432 bytes
static/fonts/iconfont.woff | Bin 0 -> 30200 bytes
static/images/aiwrap.png | Bin 0 -> 3032 bytes
static/images/bg.png | Bin 0 -> 28211 bytes
static/index.html | 10 +
static/js/jquery.min.js | 4 +
static/js/xadmin.js | 584 ++++++++++++++++++
static/js/xcity.js | 506 +++++++++++++++
static/lib/layui/css/layui.css | 2 +
static/lib/layui/css/layui.mobile.css | 2 +
static/lib/layui/css/modules/code.css | 2 +
.../css/modules/laydate/default/laydate.css | 2 +
.../css/modules/layer/default/icon-ext.png | Bin 0 -> 5911 bytes
.../layui/css/modules/layer/default/icon.png | Bin 0 -> 11493 bytes
.../layui/css/modules/layer/default/layer.css | 2 +
.../css/modules/layer/default/loading-0.gif | Bin 0 -> 5793 bytes
.../css/modules/layer/default/loading-1.gif | Bin 0 -> 701 bytes
.../css/modules/layer/default/loading-2.gif | Bin 0 -> 1787 bytes
static/lib/layui/font/iconfont.eot | Bin 0 -> 40844 bytes
static/lib/layui/font/iconfont.svg | 473 ++++++++++++++
static/lib/layui/font/iconfont.ttf | Bin 0 -> 40668 bytes
static/lib/layui/font/iconfont.woff | Bin 0 -> 26744 bytes
static/lib/layui/images/face/0.gif | Bin 0 -> 2689 bytes
static/lib/layui/images/face/1.gif | Bin 0 -> 5514 bytes
static/lib/layui/images/face/10.gif | Bin 0 -> 2797 bytes
static/lib/layui/images/face/11.gif | Bin 0 -> 4121 bytes
static/lib/layui/images/face/12.gif | Bin 0 -> 3361 bytes
static/lib/layui/images/face/13.gif | Bin 0 -> 7425 bytes
static/lib/layui/images/face/14.gif | Bin 0 -> 2375 bytes
static/lib/layui/images/face/15.gif | Bin 0 -> 1793 bytes
static/lib/layui/images/face/16.gif | Bin 0 -> 6721 bytes
static/lib/layui/images/face/17.gif | Bin 0 -> 4439 bytes
static/lib/layui/images/face/18.gif | Bin 0 -> 3017 bytes
static/lib/layui/images/face/19.gif | Bin 0 -> 3040 bytes
static/lib/layui/images/face/2.gif | Bin 0 -> 3222 bytes
static/lib/layui/images/face/20.gif | Bin 0 -> 5144 bytes
static/lib/layui/images/face/21.gif | Bin 0 -> 5191 bytes
static/lib/layui/images/face/22.gif | Bin 0 -> 9823 bytes
static/lib/layui/images/face/23.gif | Bin 0 -> 3792 bytes
static/lib/layui/images/face/24.gif | Bin 0 -> 8096 bytes
static/lib/layui/images/face/25.gif | Bin 0 -> 3127 bytes
static/lib/layui/images/face/26.gif | Bin 0 -> 3291 bytes
static/lib/layui/images/face/27.gif | Bin 0 -> 4377 bytes
static/lib/layui/images/face/28.gif | Bin 0 -> 2793 bytes
static/lib/layui/images/face/29.gif | Bin 0 -> 4854 bytes
static/lib/layui/images/face/3.gif | Bin 0 -> 4017 bytes
static/lib/layui/images/face/30.gif | Bin 0 -> 2555 bytes
static/lib/layui/images/face/31.gif | Bin 0 -> 2002 bytes
static/lib/layui/images/face/32.gif | Bin 0 -> 3481 bytes
static/lib/layui/images/face/33.gif | Bin 0 -> 2454 bytes
static/lib/layui/images/face/34.gif | Bin 0 -> 3700 bytes
static/lib/layui/images/face/35.gif | Bin 0 -> 1800 bytes
static/lib/layui/images/face/36.gif | Bin 0 -> 2331 bytes
static/lib/layui/images/face/37.gif | Bin 0 -> 1513 bytes
static/lib/layui/images/face/38.gif | Bin 0 -> 3615 bytes
static/lib/layui/images/face/39.gif | Bin 0 -> 6495 bytes
static/lib/layui/images/face/4.gif | Bin 0 -> 5689 bytes
static/lib/layui/images/face/40.gif | Bin 0 -> 3154 bytes
static/lib/layui/images/face/41.gif | Bin 0 -> 3644 bytes
static/lib/layui/images/face/42.gif | Bin 0 -> 5305 bytes
static/lib/layui/images/face/43.gif | Bin 0 -> 2674 bytes
static/lib/layui/images/face/44.gif | Bin 0 -> 4126 bytes
static/lib/layui/images/face/45.gif | Bin 0 -> 3417 bytes
static/lib/layui/images/face/46.gif | Bin 0 -> 3007 bytes
static/lib/layui/images/face/47.gif | Bin 0 -> 2333 bytes
static/lib/layui/images/face/48.gif | Bin 0 -> 2689 bytes
static/lib/layui/images/face/49.gif | Bin 0 -> 2315 bytes
static/lib/layui/images/face/5.gif | Bin 0 -> 4567 bytes
static/lib/layui/images/face/50.gif | Bin 0 -> 5866 bytes
static/lib/layui/images/face/51.gif | Bin 0 -> 2785 bytes
static/lib/layui/images/face/52.gif | Bin 0 -> 777 bytes
static/lib/layui/images/face/53.gif | Bin 0 -> 2127 bytes
static/lib/layui/images/face/54.gif | Bin 0 -> 2196 bytes
static/lib/layui/images/face/55.gif | Bin 0 -> 1971 bytes
static/lib/layui/images/face/56.gif | Bin 0 -> 2034 bytes
static/lib/layui/images/face/57.gif | Bin 0 -> 2705 bytes
static/lib/layui/images/face/58.gif | Bin 0 -> 2258 bytes
static/lib/layui/images/face/59.gif | Bin 0 -> 10311 bytes
static/lib/layui/images/face/6.gif | Bin 0 -> 2213 bytes
static/lib/layui/images/face/60.gif | Bin 0 -> 3245 bytes
static/lib/layui/images/face/61.gif | Bin 0 -> 2495 bytes
static/lib/layui/images/face/62.gif | Bin 0 -> 2017 bytes
static/lib/layui/images/face/63.gif | Bin 0 -> 5871 bytes
static/lib/layui/images/face/64.gif | Bin 0 -> 6448 bytes
static/lib/layui/images/face/65.gif | Bin 0 -> 3576 bytes
static/lib/layui/images/face/66.gif | Bin 0 -> 3029 bytes
static/lib/layui/images/face/67.gif | Bin 0 -> 2701 bytes
static/lib/layui/images/face/68.gif | Bin 0 -> 1424 bytes
static/lib/layui/images/face/69.gif | Bin 0 -> 2431 bytes
static/lib/layui/images/face/7.gif | Bin 0 -> 3398 bytes
static/lib/layui/images/face/70.gif | Bin 0 -> 4590 bytes
static/lib/layui/images/face/71.gif | Bin 0 -> 5304 bytes
static/lib/layui/images/face/8.gif | Bin 0 -> 4050 bytes
static/lib/layui/images/face/9.gif | Bin 0 -> 4221 bytes
static/lib/layui/lay/modules/carousel.js | 2 +
static/lib/layui/lay/modules/code.js | 2 +
static/lib/layui/lay/modules/colorpicker.js | 2 +
static/lib/layui/lay/modules/element.js | 2 +
static/lib/layui/lay/modules/flow.js | 2 +
static/lib/layui/lay/modules/form.js | 2 +
static/lib/layui/lay/modules/jquery.js | 5 +
static/lib/layui/lay/modules/laydate.js | 2 +
static/lib/layui/lay/modules/layedit.js | 2 +
static/lib/layui/lay/modules/layer.js | 2 +
static/lib/layui/lay/modules/laypage.js | 2 +
static/lib/layui/lay/modules/laytpl.js | 2 +
static/lib/layui/lay/modules/mobile.js | 2 +
static/lib/layui/lay/modules/rate.js | 2 +
static/lib/layui/lay/modules/slider.js | 2 +
static/lib/layui/lay/modules/table.js | 2 +
static/lib/layui/lay/modules/tree.js | 2 +
static/lib/layui/lay/modules/upload.js | 2 +
static/lib/layui/lay/modules/util.js | 2 +
static/lib/layui/layui.js | 2 +
static/welcome.html | 219 +++++++
126 files changed, 3089 insertions(+)
create mode 100755 static/css/font.css
create mode 100755 static/css/login.css
create mode 100755 static/css/theme1.css
create mode 100755 static/css/theme2.css
create mode 100755 static/css/theme3.css
create mode 100755 static/css/theme4.css
create mode 100755 static/css/theme5.css
create mode 100755 static/css/xadmin.css
create mode 100644 static/favicon.ico
create mode 100755 static/fonts/iconfont.eot
create mode 100755 static/fonts/iconfont.svg
create mode 100755 static/fonts/iconfont.ttf
create mode 100755 static/fonts/iconfont.woff
create mode 100755 static/images/aiwrap.png
create mode 100755 static/images/bg.png
create mode 100644 static/index.html
create mode 100755 static/js/jquery.min.js
create mode 100755 static/js/xadmin.js
create mode 100755 static/js/xcity.js
create mode 100755 static/lib/layui/css/layui.css
create mode 100755 static/lib/layui/css/layui.mobile.css
create mode 100755 static/lib/layui/css/modules/code.css
create mode 100755 static/lib/layui/css/modules/laydate/default/laydate.css
create mode 100755 static/lib/layui/css/modules/layer/default/icon-ext.png
create mode 100755 static/lib/layui/css/modules/layer/default/icon.png
create mode 100755 static/lib/layui/css/modules/layer/default/layer.css
create mode 100755 static/lib/layui/css/modules/layer/default/loading-0.gif
create mode 100755 static/lib/layui/css/modules/layer/default/loading-1.gif
create mode 100755 static/lib/layui/css/modules/layer/default/loading-2.gif
create mode 100755 static/lib/layui/font/iconfont.eot
create mode 100755 static/lib/layui/font/iconfont.svg
create mode 100755 static/lib/layui/font/iconfont.ttf
create mode 100755 static/lib/layui/font/iconfont.woff
create mode 100755 static/lib/layui/images/face/0.gif
create mode 100755 static/lib/layui/images/face/1.gif
create mode 100755 static/lib/layui/images/face/10.gif
create mode 100755 static/lib/layui/images/face/11.gif
create mode 100755 static/lib/layui/images/face/12.gif
create mode 100755 static/lib/layui/images/face/13.gif
create mode 100755 static/lib/layui/images/face/14.gif
create mode 100755 static/lib/layui/images/face/15.gif
create mode 100755 static/lib/layui/images/face/16.gif
create mode 100755 static/lib/layui/images/face/17.gif
create mode 100755 static/lib/layui/images/face/18.gif
create mode 100755 static/lib/layui/images/face/19.gif
create mode 100755 static/lib/layui/images/face/2.gif
create mode 100755 static/lib/layui/images/face/20.gif
create mode 100755 static/lib/layui/images/face/21.gif
create mode 100755 static/lib/layui/images/face/22.gif
create mode 100755 static/lib/layui/images/face/23.gif
create mode 100755 static/lib/layui/images/face/24.gif
create mode 100755 static/lib/layui/images/face/25.gif
create mode 100755 static/lib/layui/images/face/26.gif
create mode 100755 static/lib/layui/images/face/27.gif
create mode 100755 static/lib/layui/images/face/28.gif
create mode 100755 static/lib/layui/images/face/29.gif
create mode 100755 static/lib/layui/images/face/3.gif
create mode 100755 static/lib/layui/images/face/30.gif
create mode 100755 static/lib/layui/images/face/31.gif
create mode 100755 static/lib/layui/images/face/32.gif
create mode 100755 static/lib/layui/images/face/33.gif
create mode 100755 static/lib/layui/images/face/34.gif
create mode 100755 static/lib/layui/images/face/35.gif
create mode 100755 static/lib/layui/images/face/36.gif
create mode 100755 static/lib/layui/images/face/37.gif
create mode 100755 static/lib/layui/images/face/38.gif
create mode 100755 static/lib/layui/images/face/39.gif
create mode 100755 static/lib/layui/images/face/4.gif
create mode 100755 static/lib/layui/images/face/40.gif
create mode 100755 static/lib/layui/images/face/41.gif
create mode 100755 static/lib/layui/images/face/42.gif
create mode 100755 static/lib/layui/images/face/43.gif
create mode 100755 static/lib/layui/images/face/44.gif
create mode 100755 static/lib/layui/images/face/45.gif
create mode 100755 static/lib/layui/images/face/46.gif
create mode 100755 static/lib/layui/images/face/47.gif
create mode 100755 static/lib/layui/images/face/48.gif
create mode 100755 static/lib/layui/images/face/49.gif
create mode 100755 static/lib/layui/images/face/5.gif
create mode 100755 static/lib/layui/images/face/50.gif
create mode 100755 static/lib/layui/images/face/51.gif
create mode 100755 static/lib/layui/images/face/52.gif
create mode 100755 static/lib/layui/images/face/53.gif
create mode 100755 static/lib/layui/images/face/54.gif
create mode 100755 static/lib/layui/images/face/55.gif
create mode 100755 static/lib/layui/images/face/56.gif
create mode 100755 static/lib/layui/images/face/57.gif
create mode 100755 static/lib/layui/images/face/58.gif
create mode 100755 static/lib/layui/images/face/59.gif
create mode 100755 static/lib/layui/images/face/6.gif
create mode 100755 static/lib/layui/images/face/60.gif
create mode 100755 static/lib/layui/images/face/61.gif
create mode 100755 static/lib/layui/images/face/62.gif
create mode 100755 static/lib/layui/images/face/63.gif
create mode 100755 static/lib/layui/images/face/64.gif
create mode 100755 static/lib/layui/images/face/65.gif
create mode 100755 static/lib/layui/images/face/66.gif
create mode 100755 static/lib/layui/images/face/67.gif
create mode 100755 static/lib/layui/images/face/68.gif
create mode 100755 static/lib/layui/images/face/69.gif
create mode 100755 static/lib/layui/images/face/7.gif
create mode 100755 static/lib/layui/images/face/70.gif
create mode 100755 static/lib/layui/images/face/71.gif
create mode 100755 static/lib/layui/images/face/8.gif
create mode 100755 static/lib/layui/images/face/9.gif
create mode 100755 static/lib/layui/lay/modules/carousel.js
create mode 100755 static/lib/layui/lay/modules/code.js
create mode 100755 static/lib/layui/lay/modules/colorpicker.js
create mode 100755 static/lib/layui/lay/modules/element.js
create mode 100755 static/lib/layui/lay/modules/flow.js
create mode 100755 static/lib/layui/lay/modules/form.js
create mode 100755 static/lib/layui/lay/modules/jquery.js
create mode 100755 static/lib/layui/lay/modules/laydate.js
create mode 100755 static/lib/layui/lay/modules/layedit.js
create mode 100755 static/lib/layui/lay/modules/layer.js
create mode 100755 static/lib/layui/lay/modules/laypage.js
create mode 100755 static/lib/layui/lay/modules/laytpl.js
create mode 100755 static/lib/layui/lay/modules/mobile.js
create mode 100755 static/lib/layui/lay/modules/rate.js
create mode 100755 static/lib/layui/lay/modules/slider.js
create mode 100755 static/lib/layui/lay/modules/table.js
create mode 100755 static/lib/layui/lay/modules/tree.js
create mode 100755 static/lib/layui/lay/modules/upload.js
create mode 100755 static/lib/layui/lay/modules/util.js
create mode 100755 static/lib/layui/layui.js
create mode 100755 static/welcome.html
diff --git a/static/css/font.css b/static/css/font.css
new file mode 100755
index 00000000..b83e5b4b
--- /dev/null
+++ b/static/css/font.css
@@ -0,0 +1,16 @@
+@font-face {
+ font-family: 'iconfont';
+ src: url('../fonts/iconfont.eot');
+ src: url('../fonts/iconfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/iconfont.woff') format('woff'),
+ url('../fonts/iconfont.ttf') format('truetype'),
+ url('../fonts/iconfont.svg#iconfont') format('svg');
+}
+.iconfont{
+ font-family:"iconfont" !important;
+ font-size:16px;font-style:normal;
+ -webkit-font-smoothing: antialiased;
+ -webkit-text-stroke-width: 0.2px;
+ -moz-osx-font-smoothing: grayscale;
+}
+
diff --git a/static/css/login.css b/static/css/login.css
new file mode 100755
index 00000000..10b8f33d
--- /dev/null
+++ b/static/css/login.css
@@ -0,0 +1,105 @@
+/*
+* @Author: xuebingsi
+* @Date: 2019-04-01 13:37:17
+* @Last Modified by: zhibinm
+* @Last Modified time: 2019-04-01 13:37:19
+*/
+.login-bg{
+ /*background: #eeeeee url() 0 0 no-repeat;*/
+ background:url(../images/bg.png) no-repeat center;
+ background-size: cover;
+ overflow: hidden;
+}
+.login{
+ margin: 120px auto 0 auto;
+ min-height: 420px;
+ max-width: 420px;
+ padding: 40px;
+ background-color: #ffffff;
+ margin-left: auto;
+ margin-right: auto;
+ border-radius: 4px;
+ /* overflow-x: hidden; */
+ box-sizing: border-box;
+}
+.login a.logo{
+ display: block;
+ height: 58px;
+ width: 167px;
+ margin: 0 auto 30px auto;
+ background-size: 167px 42px;
+}
+.login .message {
+ margin: 10px 0 0 -58px;
+ padding: 18px 10px 18px 60px;
+ background: #189F92;
+ position: relative;
+ color: #fff;
+ font-size: 16px;
+}
+.login #darkbannerwrap {
+ background: url(../images/aiwrap.png);
+ width: 18px;
+ height: 10px;
+ margin: 0 0 20px -58px;
+ position: relative;
+}
+
+.login input[type=text],
+.login input[type=file],
+.login input[type=password],
+.login input[type=email], select {
+ border: 1px solid #DCDEE0;
+ vertical-align: middle;
+ border-radius: 3px;
+ height: 50px;
+ padding: 0px 16px;
+ font-size: 14px;
+ color: #555555;
+ outline:none;
+ width:100%;
+ box-sizing: border-box;
+}
+.login input[type=text]:focus,
+.login input[type=file]:focus,
+.login input[type=password]:focus,
+.login input[type=email]:focus, select:focus {
+ border: 1px solid #27A9E3;
+}
+.login input[type=submit],
+.login input[type=button]{
+ display: inline-block;
+ vertical-align: middle;
+ padding: 12px 24px;
+ margin: 0px;
+ font-size: 18px;
+ line-height: 24px;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ cursor: pointer;
+ color: #ffffff;
+ background-color: #189F92;
+ border-radius: 3px;
+ border: none;
+ -webkit-appearance: none;
+ outline:none;
+ width:100%;
+}
+.login hr {
+ background: #fff url() 0 0 no-repeat;
+}
+.login hr.hr15 {
+ height: 15px;
+ border: none;
+ margin: 0px;
+ padding: 0px;
+ width: 100%;
+}
+.login hr.hr20 {
+ height: 20px;
+ border: none;
+ margin: 0px;
+ padding: 0px;
+ width: 100%;
+}
\ No newline at end of file
diff --git a/static/css/theme1.css b/static/css/theme1.css
new file mode 100755
index 00000000..79b2dc8e
--- /dev/null
+++ b/static/css/theme1.css
@@ -0,0 +1,21 @@
+body{
+ background:#F2F1F2;
+}
+.container{
+ background:#1A1B20;
+}
+.left-nav{
+ background:#1A1B20;
+}
+
+.left-nav a{
+ color:rgba(255,255,255,.7);
+}
+..left-nav a.active{
+ background: #009688 !important;
+ color: #fff;
+}
+.left-nav a:hover{
+ background: #009688 !important;
+ color: #fff;
+}
\ No newline at end of file
diff --git a/static/css/theme2.css b/static/css/theme2.css
new file mode 100755
index 00000000..04e91717
--- /dev/null
+++ b/static/css/theme2.css
@@ -0,0 +1,21 @@
+body{
+ background:#EEF5F9;
+}
+.container{
+ background:#323640;
+}
+.left-nav{
+ background:#fff;
+}
+
+.left-nav a{
+ color:#686a76;
+}
+.left-nav a.active{
+ background: #786AED !important;
+ color: #fff;
+}
+.left-nav a:hover{
+ background: #786AED !important;
+ color: #fff;
+}
diff --git a/static/css/theme3.css b/static/css/theme3.css
new file mode 100755
index 00000000..d5919c57
--- /dev/null
+++ b/static/css/theme3.css
@@ -0,0 +1,22 @@
+body{
+ background:#E8E8E8;
+}
+.container{
+ background:#F34743;
+}
+
+.left-nav{
+ background:#F4F4F4;
+}
+
+.left-nav a{
+ color:#686a76;
+}
+.left-nav a.active{
+ background: #FEFEFE !important;
+ color: #F34743;
+}
+.left-nav a:hover{
+ background: #FEFEFE !important;
+ color: #F34743;
+}
\ No newline at end of file
diff --git a/static/css/theme4.css b/static/css/theme4.css
new file mode 100755
index 00000000..0a0ff61d
--- /dev/null
+++ b/static/css/theme4.css
@@ -0,0 +1,21 @@
+body{
+ background:#E4E4E4;
+}
+.container{
+ background:#019587;
+}
+.left-nav{
+ background:#263035;
+}
+
+.left-nav a{
+ color:#fff;
+}
+.left-nav a.active{
+ background: #212525 !important;
+ color: #fff !important;
+}
+.left-nav a:hover{
+ background: #212525 !important;
+ color: #fff !important;
+}
\ No newline at end of file
diff --git a/static/css/theme5.css b/static/css/theme5.css
new file mode 100755
index 00000000..92c9a06d
--- /dev/null
+++ b/static/css/theme5.css
@@ -0,0 +1,27 @@
+body{
+ background:#EEF5F9 !important;
+}
+.container{
+ background:linear-gradient(to left, #7b4397, #2196f3);
+}
+
+.left-nav{
+ background:#fff !important;
+}
+
+.left-nav a{
+ color:#686a76 !important;
+}
+.left-nav a.active{
+ background: linear-gradient(to left, #7c8ce4, #2196f3) !important;
+ color: #fff !important;
+ border-color: #7b4397 !important;
+}
+.left-nav a:hover{
+ background: linear-gradient(to left, #7c8ce4, #2196f3) !important;
+ color: #fff !important;
+ border-color: #7b4397 !important;
+}
+.container .logo a{
+ background: rgba(0,0,0,0) !important;
+}
\ No newline at end of file
diff --git a/static/css/xadmin.css b/static/css/xadmin.css
new file mode 100755
index 00000000..2a4d4b4b
--- /dev/null
+++ b/static/css/xadmin.css
@@ -0,0 +1,530 @@
+@charset "utf-8";
+@import url(../lib/layui/css/layui.css);
+*{
+ margin: 0px;
+ padding: 0px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+a{
+ text-decoration: none;
+}
+html{
+ width: 100%;
+ height: 100%;
+ overflow-x:hidden;
+ overflow-y:auto;
+}
+body{
+ width: 100%;
+ min-height: 100%;
+ background: #f1f1f1;
+ /*background: #fff;*/
+}
+.x-red{
+ color: red;
+}
+
+.layui-form-switch{
+ margin-top: 0px;
+}
+.layui-input:focus, .layui-textarea:focus {
+ border-color: #189f92!important;
+}
+
+.layui-fluid{
+ padding:15px;
+}
+.x-nav{
+ padding: 0 20px;
+ position: relative;
+ z-index: 99;
+ border-bottom: 1px solid #e5e5e5;
+ line-height: 39px;
+ height: 39px;
+ overflow: hidden;
+ background: #fff;
+}
+.page{
+ text-align: center;
+
+}
+.page a{
+ display: inline-block;
+ background: #fff;
+ color: #888;
+ padding: 5px;
+ min-width: 15px;
+ border: 1px solid #E2E2E2;
+
+}
+.page span{
+ display: inline-block;
+ padding: 5px;
+ min-width: 15px;
+ border: 1px solid #E2E2E2;
+}
+.page span.current{
+ display: inline-block;
+ background: #009688;
+ color: #fff;
+ padding: 5px;
+ min-width: 15px;
+ border: 1px solid #009688;
+}
+.page .pagination li{
+ display: inline-block;
+ margin-right: 5px;
+ text-align: center;
+}
+.page .pagination li.active span{
+ background: #009688;
+ color: #fff;
+ border: 1px solid #009688;
+
+}
+
+/*登录样式*/
+/*头部*/
+.container{
+ width: 100%;
+ height: 45px;
+ background-color: #222;
+}
+.container a,.layui-nav .layui-nav-item a{
+ color: #fff;
+}
+.container .logo a{
+ background-color: rgba(0,0,0,0);
+}
+.container .logo a{
+ float: left;
+ font-size: 18px;
+ padding-left: 20px;
+ line-height: 45px;
+ width: 200px;
+}
+.container .right{
+ background-color:rgba(0,0,0,0);
+ float: right;
+
+}
+.container .left_open{
+ height: 45px;
+ float: left;
+ margin-left: 10px;
+}
+.container .left_open i{
+ display: block;
+ background: rgba(255,255,255,0.1);
+ width: 32px;
+ height: 32px;
+ line-height: 32px;
+ border-radius: 3px;
+ text-align: center;
+ margin-top: 7px;
+ cursor: pointer;
+}
+.container .left_open i:hover{
+ background: rgba(255,255,255,0.3);
+}
+
+.container .left{
+ background-color:rgba(0,0,0,0);
+ float: left;
+
+}
+.container .layui-nav-item{
+ line-height: 45px;
+}
+.container .layui-nav-more{
+ top: 20px;
+}
+.container .layui-nav-child{
+ top: 50px;
+}
+.container .layui-nav-child i{
+ margin-right: 10px;
+}
+.layui-nav .layui-nav-item a{
+ cursor: pointer;
+}
+.layui-nav .layui-nav-child a{
+ color: #333;
+ cursor: pointer;
+}
+.left-nav{
+ position: absolute;
+ top: 45px;
+ bottom: 0px;
+ /*bottom: 42px;*/
+ left: 0;
+ z-index: 2;
+ padding-top: 10px;
+ background-color: #EEEEEE;
+ width: 220px;
+ max-width: 220px;
+ overflow: auto;
+ overflow-x:hidden;
+ overflow: hidden;
+
+ /*width: 0px;*/
+}
+#side-nav{
+ width: 220px;
+}
+
+.left-nav #nav li:hover > a{
+ /*color: blue;*/
+}
+.left-nav #nav .current{
+ background-color: rgba(0, 0, 0, 0.3);
+}
+.left-nav #nav li a{
+ font-size: 14px;
+ padding: 10px 15px 10px 15px;
+ display: block;
+ cursor: pointer;
+ border-left: 4px solid transparent;
+ transition: all 0.3s;
+}
+.left-nav a:hover{
+ background: #009688 !important;
+ color: #fff;
+ border-color: #04564e !important;
+}
+.left-nav a.active{
+ background: #009688 !important;
+ color: #fff;
+ border-color: #04564e !important;
+}
+.left-nav #nav li a cite{
+ font-size: 14px;
+}
+
+.left-nav #nav li .sub-menu{
+ display: none;
+}
+.left-nav #nav li .opened{
+ display: block;
+}
+.left-nav #nav li .opened:hover{
+ /*background: #fff ;*/
+}
+.left-nav #nav li .opened .current{
+
+}
+.left-nav #nav li .sub-menu li:hover{
+ /*color: blue;*/
+ /*background: #fff ;*/
+}
+.left-nav #nav li .sub-menu li a{
+ padding: 12px 15px 12px 30px;
+ font-size: 14px;
+ cursor: pointer;
+}
+.left-nav #nav li .sub-menu li .sub-menu li a{
+ padding-left: 45px;
+}
+/*.left-nav #nav li .sub-menu li a:hover{
+ color: #148cf1;
+}*/
+.left-nav #nav li .sub-menu li a i{
+ font-size: 12px;
+}
+.left-nav #nav li a i{
+ padding-right: 10px;
+ line-height: 14px;
+}
+.left-nav #nav li .nav_right{
+ float: right;
+ font-size: 16px;
+}
+.x-slide_left {
+ width: 17px;
+ height: 61px;
+ background: url(../images/icon.png) 0 0 no-repeat;
+ position: absolute;
+ top: 200px;
+ left: 220px;
+ cursor: pointer;
+ z-index: 3;
+}
+.page-content{
+ position: absolute;
+ top: 45px;
+ right: 0;
+ /*bottom: 42px;*/
+ bottom: 0px;
+ left: 220px;
+ overflow: hidden;
+ z-index: 1;
+}
+.page-content-bg{
+ position: absolute;
+ top: 45px;
+ right: 0;
+ /*bottom: 42px;*/
+ bottom: 0px;
+ left: 220px;
+ background: rgba(0,0,0,0.5);
+ overflow: hidden;
+ z-index: 100;
+ display: none;
+}
+
+.page-content .tab{
+ height: 100%;
+ width: 100%;
+ /*background: #EFEEF0;*/
+ margin: 0px;
+}
+.page-content .layui-tab-title{
+ /*padding-top: 5px;*/
+ height: 35px;
+ background: #EFEEF0 ;
+ position: relative;
+ z-index: 100;
+}
+.page-content .layui-tab-title li.home i{
+ padding-right: 5px;
+}
+.page-content .layui-tab-title li.home .layui-tab-close{
+ display: none;
+}
+.page-content .layui-tab-title li{
+ line-height: 35px;
+}
+.page-content .layui-tab-title .layui-this:after{
+ height: 36px;
+}
+.page-content .layui-tab-title li .layui-tab-close{
+ border-radius: 50%;
+}
+.page-content .layui-tab-title .layui-this{
+ background: #fff ;
+}
+.page-content .layui-tab-bar{
+ height:34px;
+ line-height: 35px;
+}
+.page-content .layui-tab-content{
+ position: absolute;
+ top: 36px;
+ bottom: 0px;
+ width: 100%;
+ padding: 0px;
+ overflow: hidden;
+}
+.page-content .layui-tab-content .layui-tab-item{
+ width: 100%;
+ height: 100%;
+
+}
+.page-content .layui-tab-content .layui-tab-item iframe{
+ width: 100%;
+ height: 100%;
+
+}
+.x-admin-carousel,.layui-carousel,.x-admin-carousel>[carousel-item]>* {
+ background-color:#fff
+}
+
+.x-admin-backlog .x-admin-backlog-body {
+ display:block;
+ padding:10px 15px;
+ background-color:#f8f8f8;
+ color:#999;
+ border-radius:2px;
+ transition:all .3s;
+ -webkit-transition:all .3s
+}
+.x-admin-backlog-body h3 {
+ padding-bottom:10px;
+ font-size:12px
+}
+.x-admin-backlog-body p cite {
+ font-style:normal;
+ font-size:30px;
+ font-weight:300;
+ color:#009688
+}
+.x-admin-backlog-body:hover {
+ background-color:#CFCFCF;
+ color:#888
+}
+
+table th, table td {
+ word-break: break-all;
+}
+
+/*404页面样式*/
+.fly-panel {
+ margin-bottom: 15px;
+ border-radius: 2px;
+ /*background-color: #fff;*/
+ box-shadow: 0 1px 2px 0 rgba(0,0,0,.05);
+}
+.fly-none {
+ min-height: 600px;
+ text-align: center;
+ padding-top: 50px;
+ color: #999;
+}
+.fly-none .layui-icon {
+ line-height: 300px;
+ font-size: 300px;
+ color: #393D49;
+}
+.fly-none p {
+ margin-top: 50px;
+ padding: 0 15px;
+ font-size: 20px;
+ color: #999;
+ font-weight: 300;
+}
+#tab_right{
+ display: none;
+ width: 80px;
+ position: absolute;
+ top: 35px;
+ left: 0px;
+}
+#tab_right dl{
+ top: 0px;
+}
+#tab_show{
+ position: absolute;
+ top: 36px;
+ bottom: 0px;
+ width: 100%;
+ background:rgb(255, 255, 255,0);
+ padding: 0px;
+ overflow: hidden;
+ display: none;
+}
+
+
+@media screen and (max-width: 768px){
+ .fast-add{
+ display: none;
+ }
+ .layui-nav .to-index{
+ display: none;
+ }
+ .container .logo a{
+ width: 140px;
+ }
+ .container .left_open {
+ /*float: right;*/
+ }
+ .left-nav{
+ width: 60px;
+ }
+ .left-nav #nav li a i{
+ font-size: 18px;
+ }
+ .left-nav cite,.left-nav .nav_right{
+ display: none;
+ }
+ .page-content{
+ left: 60px;
+ }
+ .page-content .layui-tab-content .layui-tab-item{
+ -webkit-overflow-scrolling: touch;
+ overflow-y: scroll;
+ }
+ .x-so input.layui-input{
+ width: 100%;
+ margin: 10px;
+ }
+}
+
+/*精细版样式*/
+
+.x-admin-sm{
+ font-size: 12px;
+}
+.x-admin-sm body{
+ font-size: 12px;
+}
+/*登录页面样式*/
+.x-admin-sm .login input[type=submit],.x-admin-sm .login input[type=button]{
+ font-size: 14px;
+}
+.x-admin-sm .login input[type=text],
+.x-admin-sm .login input[type=file],
+.x-admin-sm .login input[type=password],
+.x-admin-sm .login input[type=email], .x-admin-sm select {
+ font-size: 12px;
+}
+.x-admin-sm .login .message{
+ font-size: 14px;
+}
+
+.x-admin-sm .layui-table td, .x-admin-sm .layui-table th{
+ font-size: 12px;
+}
+.x-admin-sm .layui-elem-field legend{
+ font-size: 18px;
+}
+
+.x-admin-sm .x-admin-backlog-body p cite{
+ font-size: 24px;
+}
+.x-admin-sm .left-nav #nav li a cite{
+ font-size: 12px;
+}
+.x-admin-sm .iconfont{
+ font-size: 14px;
+}
+.x-admin-sm .layui-tab-title li{
+ font-size: 12px;
+}
+.x-admin-sm .layui-icon{
+ font-size: 14px;
+}
+.x-admin-sm .layui-nav *{
+ font-size: 12px;
+}
+.x-admin-sm .layui-breadcrumb>*{
+ font-size: 12px;
+}
+.x-admin-sm .layui-btn,.x-admin-sm .layui-btn-xs,.x-admin-sm .layui-btn-sm{
+ font-size: 12px;
+}
+
+.x-admin-sm .layui-laydate{
+ font-size: 12px;
+}
+.x-admin-sm .layui-btn{
+ height: 30px;
+ line-height: 30px;
+ padding: 0 10px;
+}
+
+.x-admin-sm .layui-btn-lg{
+ height: 38px;
+ line-height: 38px;
+ padding: 0 18px;
+ font-size: 14px;
+}
+.x-admin-sm .layui-layer-title,.x-admin-sm .layui-layer-dialog .layui-layer-content{
+ font-size: 12px;
+}
+.x-admin-sm .layui-input,.x-admin-sm .layui-select,.x-admin-sm .layui-textarea{
+ height: 30px;
+}
+
+.x-admin-sm .layui-form-pane .layui-form-label{
+ height: 30px;
+ line-height: 14px;
+}
+.x-admin-sm .layui-form-checkbox span{
+ font-size: 12px;
+}
+.x-admin-sm .fly-none .layui-icon {
+ line-height: 300px;
+ font-size: 300px;
+ color: #393D49;
+}
+
diff --git a/static/favicon.ico b/static/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..6ef2663d76572c1cddba652046c0846e667cfce2
GIT binary patch
literal 16958
zcmeI2T}WGJ7{|{fh)9tjBBc~3#o9Er)~2?mn(Ap9ZEBr}xLp(}B2uJ?lppgVc<53mzz=aOF(Sa2PT8!ku!1Pqb
z4m4mU7kXf@om?2sq}YWo!S9ae1{c*E^NS7RFenVbfcWj$SzolSm^|1{Js5?-H5jPk
z9&}Jd{4M0dE%BUOXr1v?U53E`4C=)kdC-dKr<_JTxzL9WhG9@9)-wG-0~V|)uw?sJ
zS7D%OeXEPm!5OSztF=s@3*4>WM*O3gwg+tG?_AKVLkGQEt%*76flB;o?@xE1oX>0m
z)WkgMg61SOVbi{m*`B|@chtlZf3&U_2I9}O^hxHcFfb(&>ifTVhQ2~@v0*3sf{~hF
zr9Ue2$1EM}jpt#YvNsG7f5lv=rj)&*n*FgVL&JDyx7Zu=*&D}T&x`hXNSb
zF|Bt_cYQX@hacFALibNx3@uqlLieoI^hrC@`R$9;<$5EvW*7{igFeiZ?%Hgb4=*_PE
z^N$sB;T8-gU~r3?*b9R(7;Nh4sfk@M7$*MnzKD5UH`x@w@D&C+7}UZ*(*SvpPd#u6
z2GoPjt>PgU^4J?q^hcX?v(>ekJQsW1Tp4?Gssjf7kyU$<@HPCPF^_sM9=dNEgTYm-
z7#$er7msgC#eyEhXUBFJ^ojl;5UEWqWz`see7ZFDsJSop@KpOo$Ek^z;4q2~1cNWh
z1vdfq{KC@~13DNX
z7p%murzZBWFI-@6w6Ql{iYzylkP9U+7(@q~>SeaaF8ojY*rvpP=uluFYM}`Rm&t{y
zwf^8H4BDv&1=K~|@=wWL>HiRSx)|b*n=4?T^}8^zrn^qgO9GOBBp?Y$0+N6vAPGnU
zl7J*22}lBxfFvLZydQzL8Grn`=HZ(^UVf&X{_^rqF}}I%*2Whv5^I+vwB@%~lEXhA
z6Pu@ArCxb$`sdX0mE_6mhUEC^-Q@Uta(MgO+L&5@{o&fg+K~8b^Yd4!r-{@_2919M
Dk2;%q
literal 0
HcmV?d00001
diff --git a/static/fonts/iconfont.eot b/static/fonts/iconfont.eot
new file mode 100755
index 0000000000000000000000000000000000000000..df5334af5f88dbaafc510fc0b94ecd79baab2ee6
GIT binary patch
literal 49600
zcmd3P2Y?&J+4jzCSC>}ON>_BJ(@DDGYq~q>+|Iq?f^BSr4aOZ4V~1kEn05hUdIu9~
z0-?nu0RkbU0SOQYHQ}Qql!W$42c$qCfsdT_{^#A*xqwN+|9yWsX-2!VyR*A9@4WSy
zxrcr(2r(okaDo1~AdB_}N-klvHRnKQe@|`~V^>$+_wOkk*$N0p3TuR~3Y&%XcD@A%
ze8NIuop6M3yl|ATN;noJQDF&6juno>*>rBb&?U4Boj9_;>rqTMbqfQ6D0I$Pw0QDe
z);)hhVIN8-&tKG$TWZWc4Ev{Gf61CntB%gTaD71#q*1u2>4>9FTEFJ5yZ(W?w+q5;
zA8lB-YHgry*{^Z#>qz+xDA1?M7i0UUNR1mdZP~i~=HXjW-}7jZgOB>^npMWxrCoyH
zKL};*n^tW-n%ymYitQNAuiw0C)4KkHtWyQy{;fEF)6ri&Zp-XX*F7r;KOPhWrH!6l
z92g!D4wcUtes%bL`CR*LaOv*9opuS%^S`g?hx^Cj&0!55xBeN$sB-SbSJITtiJa!fu4l?tF$kQNfgKOK^49GVlOC?)C}g