Fixed some diagnostics warnings

Moved examples to tofix because fixing them is besides the point right
now.
This commit is contained in:
Dawid Sobczak 2023-09-19 11:42:10 +01:00
parent 52164c82e3
commit 858fe11666
166 changed files with 68 additions and 264 deletions

3
06/deps/lpeglabel/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
lpeglabel.so
lpeglabel.a
*.o

32
06/deps/lpeglabel/HISTORY Normal file
View file

@ -0,0 +1,32 @@
HISTORY for LPegLabel 1.2.0-1
* Changes from version 1.1.0-1 to 1.2.0-1
---------------------------------
+ fixes bug when reporting the error position of an ordinary failure
+ reports the farthest failure position for ordinary failures
* Changes from version 1.0.0-1 to 1.1.0-1
---------------------------------
+ new semantics of lpeglabel.Rec
- labeled ordered choice removed
+ examples updated
* Changes from version 0.12.2-2 to 1.0.0-1
---------------------------------
+ base code updated to LPeg 1.0
+ new functions: lpeglabel.Rec and relabel.calcline
+ improved documentation
+ new examples
+ some bugs fixed
* Changes from version 0.12.2-1 to 0.12.2-2
---------------------------------
+ in case of a failure, "match" also returns a suffix of the input
+ improved error reporting for "relabel", thanks to Matthew Allen
+ limit of labels increased from 32 to 64, thanks to André Maidl
+ compiles with Lua 5.1, thanks to Matthew Allen
+ the throw operator now throws only one label
+ some bugs fixed

22
06/deps/lpeglabel/LICENSE Normal file
View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014-2016 Sérgio Medeiros
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.

727
06/deps/lpeglabel/README.md Normal file
View file

@ -0,0 +1,727 @@
<p align="center"><img src="https://github.com/sqmedeiros/lpeglabel/raw/master/lpeglabel-logo.png" alt="LPegLabel" width="150px"></p>
## LPegLabel - Parsing Expression Grammars (with Labels) for Lua
---
### Introduction
LPegLabel is a conservative extension of the
[LPeg](http://www.inf.puc-rio.br/~roberto/lpeg)
library that provides an implementation of Parsing
Expression Grammars (PEGs) with labeled failures.
Labels can be used to signal different kinds of errors
and to specify which recovery pattern should handle a
given label. Labels can also be combined with the standard
patterns of LPeg.
Besides that, LPegLabel also reports the farthest
failure position in case of an ordinary failure
(which is represented by label **0**).
This document describes the new functions available
in LpegLabel and presents some examples of usage.
With labeled failures it is possible to distinguish
between an ordinary failure and an error. Usually, an
ordinary failure is produced when the matching of a
character fails, and this failure is caught by ordered choice.
An error (a non-ordinary failure), by its turn, is produced
by the throw operator and may be caught by the recovery operator.
In LPegLabel, the result of an unsuccessful matching
is a triple **nil, lab, sfail**, where **lab**
is the label associated with the failure, and
**sfail** is the suffix input being matched when
**lab** was thrown.
When **lab** is an ordinary failure and no error was thrown before,
**sfail** is formed according to the farthest position where an
ordinary failure occurred.
In case **lab** is an ordinary failure and an error
was thrown before, **sfail** is the farthest suffix
where an ordinary failure occurred after the last error.
Below there is a brief summary of the new functions provided by LpegLabel:
<table border="1">
<tbody><tr><td><b>Function</b></td><td><b>Description</b></td></tr>
<tr><td><a href="#f-t"><code>lpeglabel.T (l)</code></a></td>
<td>Throws a label <code>l</code> to signal an error</td></tr>
<tr><td><a href="#f-rec"><code>lpeglabel.Rec (p1, p2, l1 [, l2, ..., ln])</code></a></td>
<td>Specifies a recovery pattern <code>p2</code> for <code>p1</code>,
when the matching of <code>p1</code> gives one of the labels l1, ..., ln.</td></tr>
<tr><td><a href="#re-t"><code>%{l}</code></a></td>
<td>Syntax of <em>relabel</em> module. Equivalent to <code>lpeglabel.T(l)</code>
</td></tr>
<tr><td><a href="#re-rec"><code>p1 //{l1 [, l2, ..., ln} p2</code></a></td>
<td>Syntax of <em>relabel</em> module. Equivalent to <code>lpeglabel.Rec(p1, p2, l1, ..., ln)</code>
</td></tr>
<tr><td><a href="#re-line"><code>relabel.calcline(subject, i)</code></a></td>
<td>Calculates line and column information regarding position <i>i</i> of the subject</code>
</td></tr>
<tr><td><a href="#re-setl"><code>relabel.setlabels (tlabel)</code></a></td>
<td>Allows to specicify a table with mnemonic labels.
</td></tr>
</tbody></table>
### Functions
#### <a name="f-t"></a><code>lpeglabel.T(l)</code>
Returns a pattern that throws the label `l`.
A label must be an integer between 1 and 255.
This pattern always causes a failure, whose associated
position will be used to set **sfail**, no matter
whether this is the farthest failure position or not.
#### <a name="f-rec"></a><code>lpeglabel.Rec(p1, p2, l1, ..., ln)</code>
Returns a *recovery pattern*.
If the matching of `p1` gives one of the labels `l1, ..., ln`,
then the matching of `p2` is tried from the failure position of `p1`.
Otherwise, the result of the matching of `p1` is the pattern's result.
#### <a name="re-t"></a><code>%{l}</code>
Syntax of *relabel* module. Equivalent to `lpeg.T(l)`.
#### <a name="re-lc"></a><code>p1 //{l1, ..., ln} p2</code>
Syntax of *relabel* module. Equivalent to `lpeglabel.Rec(p1, p2, l1, ..., ln)`.
The `//{}` operator is left-associative.
#### <a name="re-line"></a><code>relabel.calcline (subject, i)</code>
Returns line and column information regarding position <i>i</i> of the subject.
#### <a name="re-setl"></a><code>relabel.setlabels (tlabel)</code>
Allows to specicify a table with labels. They keys of
`tlabel` must be integers between 1 and 255,
and the associated values should be strings.
### Examples
Below there a few examples of usage of LPegLabel.
The code of these and of other examples is available
in the *examples* directory.
#### Reporting the farthest failure
This example illustrates the new values returned
by the *match* function in case of an unsuccessful
matching. As no error is thrown, when the matching
fails *sfail* represents the farthest suffix where
an ordinary failure occurred.
```lua
local m = require'lpeglabel'
function matchPrint(p, s)
local r, lab, sfail = p:match(s)
print("r: ", r, "lab: ", lab, "sfail: ", sfail)
end
local p = m.P"a"^0 * m.P"b" + m.P"c"
matchPrint(p, "abc") --> r: 3 lab: nil sfail: nil
matchPrint(p, "c") --> r: 2 lab: nil sfail: nil
matchPrint(p, "aac") --> r: nil lab: 0 sfail: c
matchPrint(p, "xxc") --> r: nil lab: 0 sfail: xxc
```
#### Matching a list of identifiers separated by commas
The following example defines a grammar that matches
a list of identifiers separated by commas. A label
is thrown when there is an error matching an identifier
or a comma.
We use function `newError` to store error messages in a
table and to return the index associated with each error message.
```lua
local m = require'lpeglabel'
local re = require'relabel'
local terror = {}
local function newError(s)
table.insert(terror, s)
return #terror
end
local errUndef = newError("undefined")
local errId = newError("expecting an identifier")
local errComma = newError("expecting ','")
local g = m.P{
"S",
S = m.V"Id" * m.V"List",
List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List",
Id = m.V"Sp" * m.R'az'^1,
Comma = m.V"Sp" * ",",
Sp = m.S" \n\t"^0,
}
function mymatch (g, s)
local r, e, sfail = g:match(s)
if not r then
local line, col = re.calcline(s, #s - #sfail)
local msg = "Error at line " .. line .. " (col " .. col .. "): "
return r, msg .. terror[e] .. " before '" .. sfail .. "'"
end
return r
end
print(mymatch(g, "one,two")) --> 8
print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expecting ',' before ' two'
print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before ''
```
In this example we could think about writing rule <em>List</em> as follows:
```lua
List = ((m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)))^0,
```
but when matching this expression against the end of input
we would get a failure whose associated label would be **errComma**,
and this would cause the failure of the *whole* repetition.
#### Error Recovery
By using the `Rec` function we can specify a recovery pattern that
should be matched when a label is thrown. After matching the recovery
pattern, and possibly recording the error, the parser will resume
the <em>regular</em> matching. For example, in the example below
we expect to match rule `A`, but when a failure occur the label 42
is thrown and then we will try to match the recovery pattern `recp`:
```lua
local m = require'lpeglabel'
local recp = m.P"oast"
local g = m.P{
"S",
S = m.Rec(m.V"A", recp, 42) * ".",
A = m.P"t" * (m.P"est" + m.T(42))
}
print(g:match("test.")) --> 6
print(g:match("toast.")) --> 7
print(g:match("oast.")) --> nil 0 oast.
print(g:match("toward.")) --> nil 0 ward.
```
When trying to match subject 'toast.', in rule `A` the first
't' is matched, then the matching of `m.P"est"` fails and label 42
is thrown, with the associated inpux suffix 'oast.'. In rule
`S` label 42 is caught and the recovery pattern matches 'oast',
so pattern `'.'` matches the rest of the input.
When matching subject 'oast.', pattern `m.P"t"` fails, and
the result of the matching is <b>nil, 0, oast.</b>.
When matching 'toward.', label 42 is thrown after matching 't',
with the associated input suffix 'oward.'. As the matching of the
recovery pattern fails, the result is <b>nil, 0, ward.</b>.
Usually, the recovery pattern is an expression that does not fail.
In the previous example, we could have used `(m.P(1) - m.P".")^0`
as the recovery pattern.
Below we rewrite the grammar that describes a list of identifiers
to use a recovery strategy. Grammar `g` remains the same, but we add a
recovery grammar `grec` that handles the labels thrown by `g`.
In grammar `grec` we use functions `record` and `sync`.
Function `record`, plus function `recorderror`, will help
us to save the input position where a label was thrown,
while function `sync` will give us a synchronization pattern,
that consumes the input while is not possible to match a given
pattern `p`.
When the matching of an identifier fails, a defaul value ('NONE')
is provided.
```lua
local m = require'lpeglabel'
local re = require'relabel'
local terror = {}
local function newError(s)
table.insert(terror, s)
return #terror
end
local errUndef = newError("undefined")
local errId = newError("expecting an identifier")
local errComma = newError("expecting ','")
local id = m.R'az'^1
local g = m.P{
"S",
S = m.V"Id" * m.V"List",
List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
Id = m.V"Sp" * id + m.T(errId),
Comma = m.V"Sp" * "," + m.T(errComma),
Sp = m.S" \n\t"^0,
}
local subject, errors
function recorderror(pos, lab)
local line, col = re.calcline(subject, pos)
table.insert(errors, { line = line, col = col, msg = terror[lab] })
end
function record (lab)
return (m.Cp() * m.Cc(lab)) / recorderror
end
function sync (p)
return (-p * m.P(1))^0
end
local grec = m.P{
"S",
S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId),
ErrComma = record(errComma) * sync(id),
ErrId = record(errId) * sync(m.P",")
}
function mymatch (g, s)
errors = {}
subject = s
local r, e, sfail = g:match(s)
if #errors > 0 then
local out = {}
for i, err in ipairs(errors) do
local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
table.insert(out, msg)
end
return nil, table.concat(out, "\n") .. "\n"
end
return r
end
print(mymatch(grec, "one,two"))
-- Captures (separated by ';'): one; two;
-- Syntactic errors found: 0
print(mymatch(grec, "one two three"))
-- Captures (separated by ';'): one; two; three;
-- Syntactic errors found: 2
-- Error at line 1 (col 4): expecting ','
-- Error at line 1 (col 8): expecting ','
print(mymatch(grec, "1,\n two, \n3,"))
-- Captures (separated by ';'): NONE; two; NONE; NONE;
-- Syntactic errors found: 3
-- Error at line 1 (col 1): expecting an identifier
-- Error at line 2 (col 6): expecting an identifier
-- Error at line 3 (col 2): expecting an identifier
print(mymatch(grec, "one\n two123, \nthree,"))
-- Captures (separated by ';'): one; two; three; NONE;
-- Syntactic errors found: 3
-- Error at line 2 (col 1): expecting ','
-- Error at line 2 (col 5): expecting ','
-- Error at line 3 (col 6): expecting an identifier
```
##### *relabel* syntax
Below we describe again a grammar that matches a list of identifiers,
now using the syntax supported by *relabel*, where `//{}` is the
recovery operator, and `%{}` is the throw operator:
```lua
local re = require 'relabel'
local errinfo = {
{"errUndef", "undefined"},
{"errId", "expecting an identifier"},
{"errComma", "expecting ','"},
}
local errmsgs = {}
local labels = {}
for i, err in ipairs(errinfo) do
errmsgs[i] = err[2]
labels[err[1]] = i
end
re.setlabels(labels)
local g = re.compile[[
S <- Id List
List <- !. / Comma Id List
Id <- Sp {[a-z]+} / %{errId}
Comma <- Sp ',' / %{errComma}
Sp <- %s*
]]
local errors
function recorderror (subject, pos, label)
local line, col = re.calcline(subject, pos)
table.insert(errors, { line = line, col = col, msg = errmsgs[labels[label]] })
return true
end
function sync (p)
return '( !(' .. p .. ') .)*'
end
local grec = re.compile(
"S <- %g //{errComma} ErrComma //{errId} ErrId" .. "\n" ..
"ErrComma <- ('' -> 'errComma' => recorderror) " .. sync('[a-z]+') .. "\n" ..
"ErrId <- ('' -> 'errId' => recorderror) " .. sync('","') .. "-> default"
, {g = g, recorderror = recorderror, default = "NONE"}
)
function mymatch (g, s)
errors = {}
subject = s
io.write("Input: ", s, "\n")
local r = { g:match(s) }
io.write("Captures (separated by ';'): ")
for k, v in pairs(r) do
io.write(v .. "; ")
end
io.write("\nSyntactic errors found: " .. #errors)
if #errors > 0 then
io.write("\n")
local out = {}
for i, err in ipairs(errors) do
local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
table.insert(out, msg)
end
io.write(table.concat(out, "\n"))
end
print("\n")
return r
end
print(mymatch(grec, "one,two"))
-- Captures (separated by ';'): one; two;
-- Syntactic errors found: 0
print(mymatch(grec, "one two three"))
-- Captures (separated by ';'): one; two; three;
-- Syntactic errors found: 2
-- Error at line 1 (col 4): expecting ','
-- Error at line 1 (col 8): expecting ','
print(mymatch(grec, "1,\n two, \n3,"))
-- Captures (separated by ';'): NONE; two; NONE; NONE;
-- Syntactic errors found: 3
-- Error at line 1 (col 1): expecting an identifier
-- Error at line 2 (col 6): expecting an identifier
-- Error at line 3 (col 2): expecting an identifier
print(mymatch(grec, "one\n two123, \nthree,"))
-- Captures (separated by ';'): one; two; three; NONE;
-- Syntactic errors found: 3
-- Error at line 2 (col 1): expecting ','
-- Error at line 2 (col 5): expecting ','
-- Error at line 3 (col 6): expecting an identifier
```
#### Arithmetic Expressions
Here's an example of an LPegLabel grammar that matches an expression.
We have used a function `expect`, that takes a pattern `patt` and a label as
parameters and builds a new pattern that throws this label when `patt`
fails.
When a subexpression is syntactically invalid, a default value of 1000
is provided by the recovery pattern, so the evaluation of an expression
should always produce a numeric value.
In this example, we can see that it may be a tedious and error prone
task to build manually the recovery grammar `grec`. In the next example
we will show how to build the recovery grammar in a more automatic way.
```lua
local m = require"lpeglabel"
local re = require"relabel"
local labels = {
{"ExpTermFirst", "expected an expression"},
{"ExpTermOp", "expected a term after the operator"},
{"MisClose", "missing a closing ')' after the expression"},
}
local function labelindex(labname)
for i, elem in ipairs(labels) do
if elem[1] == labname then
return i
end
end
error("could not find label: " .. labname)
end
local errors, subject
local function expect(patt, labname)
local i = labelindex(labname)
return patt + m.T(i)
end
local num = m.R("09")^1 / tonumber
local op = m.S("+-")
local function compute(tokens)
local result = tokens[1]
for i = 2, #tokens, 2 do
if tokens[i] == '+' then
result = result + tokens[i+1]
elseif tokens[i] == '-' then
result = result - tokens[i+1]
else
error('unknown operation: ' .. tokens[i])
end
end
return result
end
local g = m.P {
"Exp",
Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute,
OperandFirst = expect(m.V"Term", "ExpTermFirst"),
Operand = expect(m.V"Term", "ExpTermOp"),
Term = num + m.V"Group",
Group = "(" * m.V"Exp" * expect(")", "MisClose"),
}
function recorderror(pos, lab)
local line, col = re.calcline(subject, pos)
table.insert(errors, { line = line, col = col, msg = labels[lab][2] })
end
function record (labname)
return (m.Cp() * m.Cc(labelindex(labname))) / recorderror
end
function sync (p)
return (-p * m.P(1))^0
end
function defaultValue (p)
return p or m.Cc(1000)
end
local grec = m.P {
"S",
S = m.Rec(m.V"A", m.V"ErrExpTermFirst", labelindex("ExpTermFirst")),
A = m.Rec(m.V"Sg", m.V"ErrExpTermOp", labelindex("ExpTermOp")),
Sg = m.Rec(g, m.V"ErrMisClose", labelindex("MisClose")),
ErrExpTermFirst = record("ExpTermFirst") * sync(op + ")") * defaultValue(),
ErrExpTermOp = record("ExpTermOp") * sync(op + ")") * defaultValue(),
ErrMisClose = record("MisClose") * sync(m.P")") * defaultValue(m.P""),
}
local function eval(input)
errors = {}
io.write("Input: ", input, "\n")
subject = input
local result, label, suffix = grec:match(input)
io.write("Syntactic errors found: " .. #errors, "\n")
if #errors > 0 then
local out = {}
for i, err in ipairs(errors) do
local pos = err.col
local msg = err.msg
table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")")
end
print(table.concat(out, "\n"))
end
io.write("Result = ")
return result
end
print(eval "90-70-(5)+3")
-- Syntactic errors found: 0
-- Result = 18
print(eval "15+")
-- Syntactic errors found: 1
-- syntax error: expected a term after the operator (at index 3)
-- Result = 1015
print(eval "-2")
-- Syntactic errors found: 1
-- syntax error: expected an expression (at index 1)
-- Result = 998
print(eval "1+()+")
-- Syntactic errors found: 2
-- syntax error: expected an expression (at index 4)
-- syntax error: expected a term after the operator (at index 5)
-- Result = 2001
print(eval "1+(")
-- Syntactic errors found: 2
-- syntax error: expected an expression (at index 3)
-- syntax error: missing a closing ')' after the expression (at index 3)
-- Result = 1001
print(eval "3)")
-- Syntactic errors found: 0
-- Result = 3
```
#### Automatically Building the Recovery Grammar
Below we rewrite the previous example to automatically
build the recovery grammar based on information provided
by the user for each label (error message, recovery pattern, etc).
In the example below we also throw an error when the grammar
does not match the whole subject.
```lua
local m = require"lpeglabel"
local re = require"relabel"
local num = m.R("09")^1 / tonumber
local op = m.S("+-")
local labels = {}
local nlabels = 0
local function newError(lab, msg, psync, pcap)
nlabels = nlabels + 1
psync = psync or m.P(-1)
pcap = pcap or m.P""
labels[lab] = { id = nlabels, msg = msg, psync = psync, pcap = pcap }
end
newError("ExpTermFirst", "expected an expression", op + ")", m.Cc(1000))
newError("ExpTermOp", "expected a term after the operator", op + ")", m.Cc(1000))
newError("MisClose", "missing a closing ')' after the expression", m.P")")
newError("Extra", "extra characters found after the expression")
local errors, subject
local function expect(patt, labname)
local i = labels[labname].id
return patt + m.T(i)
end
local function compute(tokens)
local result = tokens[1]
for i = 2, #tokens, 2 do
if tokens[i] == '+' then
result = result + tokens[i+1]
elseif tokens[i] == '-' then
result = result - tokens[i+1]
else
error('unknown operation: ' .. tokens[i])
end
end
return result
end
local g = m.P {
"Exp",
Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute,
OperandFirst = expect(m.V"Term", "ExpTermFirst"),
Operand = expect(m.V"Term", "ExpTermOp"),
Term = num + m.V"Group",
Group = "(" * m.V"Exp" * expect(")", "MisClose"),
}
function recorderror(pos, lab)
local line, col = re.calcline(subject, pos)
table.insert(errors, { line = line, col = col, msg = labels[lab].msg })
end
function record (labname)
return (m.Cp() * m.Cc(labname)) / recorderror
end
function sync (p)
return (-p * m.P(1))^0
end
function defaultValue (p)
return p or m.Cc(1000)
end
local grec = g * expect(m.P(-1), "Extra")
for k, v in pairs(labels) do
grec = m.Rec(grec, record(k) * sync(v.psync) * v.pcap, v.id)
end
local function eval(input)
errors = {}
io.write("Input: ", input, "\n")
subject = input
local result, label, suffix = grec:match(input)
io.write("Syntactic errors found: " .. #errors, "\n")
if #errors > 0 then
local out = {}
for i, err in ipairs(errors) do
local pos = err.col
local msg = err.msg
table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")")
end
print(table.concat(out, "\n"))
end
io.write("Result = ")
return result
end
print(eval "90-70-(5)+3")
-- Syntactic errors found: 0
-- Result = 18
print(eval "15+")
-- Syntactic errors found: 1
-- syntax error: expected a term after the operator (at index 3)
-- Result = 1015
print(eval "-2")
-- Syntactic errors found: 1
-- syntax error: expected an expression (at index 1)
-- Result = 998
print(eval "1+()+")
-- Syntactic errors found: 2
-- syntax error: expected an expression (at index 4)
-- syntax error: expected a term after the operator (at index 5)
-- Result = 2001
print(eval "1+(")
-- Syntactic errors found: 2
-- syntax error: expected an expression (at index 3)
-- syntax error: missing a closing ')' after the expression (at index 3)
-- Result = 1001
print(eval "3)")
-- Syntactic errors found: 1
-- syntax error: extra characters found after the expression (at index 2)
-- Result = 3
```

View file

@ -0,0 +1,128 @@
local m = require "lpeglabel"
local re = require "relabel"
local labels = {
{"ExpTermFirst", "expected an expression"},
{"ExpTermOp", "expected a term after the operator"},
{"MisClose", "missing a closing ')' after the expression"},
}
local function labelindex(labname)
for i, elem in ipairs(labels) do
if elem[1] == labname then
return i
end
end
error("could not find label: " .. labname)
end
local errors, subject
local function expect(patt, labname)
local i = labelindex(labname)
return patt + m.T(i)
end
local num = m.R("09")^1 / tonumber
local op = m.S("+-")
local function compute(tokens)
local result = tokens[1]
for i = 2, #tokens, 2 do
if tokens[i] == '+' then
result = result + tokens[i+1]
elseif tokens[i] == '-' then
result = result - tokens[i+1]
else
error('unknown operation: ' .. tokens[i])
end
end
return result
end
local g = m.P {
"Exp",
Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute,
OperandFirst = expect(m.V"Term", "ExpTermFirst"),
Operand = expect(m.V"Term", "ExpTermOp"),
Term = num + m.V"Group",
Group = "(" * m.V"Exp" * expect(")", "MisClose"),
}
function recorderror(pos, lab)
local line, col = re.calcline(subject, pos)
table.insert(errors, { line = line, col = col, msg = labels[lab][2] })
end
function record (labname)
return (m.Cp() * m.Cc(labelindex(labname))) / recorderror
end
function sync (p)
return (-p * m.P(1))^0
end
function defaultValue (p)
return p or m.Cc(1000)
end
local grec = m.P {
"S",
S = m.Rec(m.V"A", m.V"ErrExpTermFirst", labelindex("ExpTermFirst")), -- default value is 0
A = m.Rec(m.V"Sg", m.V"ErrExpTermOp", labelindex("ExpTermOp")),
Sg = m.Rec(g, m.V"ErrMisClose", labelindex("MisClose")),
ErrExpTermFirst = record("ExpTermFirst") * sync(op + ")") * defaultValue(),
ErrExpTermOp = record("ExpTermOp") * sync(op + ")") * defaultValue(),
ErrMisClose = record("MisClose") * sync(m.P")") * defaultValue(m.P""),
}
local function eval(input)
errors = {}
io.write("Input: ", input, "\n")
subject = input
local result, label, suffix = grec:match(input)
io.write("Syntactic errors found: " .. #errors, "\n")
if #errors > 0 then
local out = {}
for i, err in ipairs(errors) do
local pos = err.col
local msg = err.msg
table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")")
end
print(table.concat(out, "\n"))
end
io.write("Result = ")
return result
end
print(eval "90-70-(5)+3")
--> 20
print(eval "15+")
--> 2 + 0
print(eval "-2")
--> 0 - 2
print(eval "1+3+-9")
--> 1 + 3 + [0] - 9
print(eval "1+()3+")
--> 1 + ([0]) [3 +] [0]
print(eval "8-(2+)-5")
--> 8 - (2 + [0]) - 5
print(eval "()")
print(eval "")
print(eval "1+()+")
print(eval "1+(")
print(eval "3)")
print(eval "11+())3")

View file

@ -0,0 +1,122 @@
local m = require "lpeglabel"
local re = require "relabel"
local num = m.R("09")^1 / tonumber
local op = m.S("+-")
local labels = {}
local nlabels = 0
local function newError(lab, msg, psync, pcap)
nlabels = nlabels + 1
psync = psync or m.P(-1)
pcap = pcap or m.P""
labels[lab] = { id = nlabels, msg = msg, psync = psync, pcap = pcap }
end
newError("ExpTermFirst", "expected an expression", op + ")", m.Cc(1000))
newError("ExpTermOp", "expected a term after the operator", op + ")", m.Cc(1000))
newError("MisClose", "missing a closing ')' after the expression", m.P")")
newError("Extra", "extra characters found after the expression")
local errors, subject
local function expect(patt, labname)
local i = labels[labname].id
return patt + m.T(i)
end
local function compute(tokens)
local result = tokens[1]
for i = 2, #tokens, 2 do
if tokens[i] == '+' then
result = result + tokens[i+1]
elseif tokens[i] == '-' then
result = result - tokens[i+1]
else
error('unknown operation: ' .. tokens[i])
end
end
return result
end
local g = m.P {
"Exp",
Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute,
OperandFirst = expect(m.V"Term", "ExpTermFirst"),
Operand = expect(m.V"Term", "ExpTermOp"),
Term = num + m.V"Group",
Group = "(" * m.V"Exp" * expect(")", "MisClose"),
}
function recorderror(pos, lab)
local line, col = re.calcline(subject, pos)
table.insert(errors, { line = line, col = col, msg = labels[lab].msg })
end
function record (labname)
return (m.Cp() * m.Cc(labname)) / recorderror
end
function sync (p)
return (-p * m.P(1))^0
end
function defaultValue (p)
return p or m.Cc(1000)
end
local grec = g * expect(m.P(-1), "Extra")
for k, v in pairs(labels) do
grec = m.Rec(grec, record(k) * sync(v.psync) * v.pcap, v.id)
end
local function eval(input)
errors = {}
io.write("Input: ", input, "\n")
subject = input
local result, label, suffix = grec:match(input)
io.write("Syntactic errors found: " .. #errors, "\n")
if #errors > 0 then
local out = {}
for i, err in ipairs(errors) do
local pos = err.col
local msg = err.msg
table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")")
end
print(table.concat(out, "\n"))
end
io.write("Result = ")
return result
end
print(eval "90-70-(5)+3")
--> 18
print(eval "15+")
--> 2 + 0
print(eval "-2")
--> 0 - 2
print(eval "1+3+-9")
--> 1 + 3 + [0] - 9
print(eval "1+()3+")
--> 1 + ([0]) [+] 3 + [0]
print(eval "8-(2+)-5")
--> 8 - (2 + [0]) - 5
print(eval "()")
print(eval "")
print(eval "1+()+")
print(eval "1+(")
print(eval "3)")
print(eval "11+()3")
--> 1 + ([0]) [+] 3 + [0]

View file

@ -0,0 +1,14 @@
local m = require'lpeglabel'
function matchPrint(p, s)
local r, lab, sfail = p:match(s)
print("r: ", r, "lab: ", lab, "sfail: ", sfail)
end
local p = m.P"a"^0 * m.P"b" + m.P"c"
matchPrint(p, "abc") --> r: 3 lab: nil sfail: nil
matchPrint(p, "c") --> r: 2 lab: nil sfail: nil
matchPrint(p, "aac") --> r: nil lab: 0 sfail: c
matchPrint(p, "xxc") --> r: nil lab: 0 sfail: xxc

View file

@ -0,0 +1,33 @@
local m = require'lpeglabel'
local re = require'relabel'
local id = m.R'az'^1
local g = m.P{
"S",
S = m.V"Id" * m.V"List",
List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
Id = m.V"Sp" * id + m.T(1),
Comma = m.V"Sp" * "," + m.T(2),
Sp = m.S" \n\t"^0,
}
function mymatch (g, s)
local r, e, sfail = g:match(s)
if not r then
local line, col = re.calcline(s, #s - #sfail)
local msg = "Error at line " .. line .. " (col " .. col .. ")"
if e == 1 then
return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
elseif e == 2 then
return r, msg .. ": expecting ',' before '" .. sfail .. "'"
else
return r, msg
end
end
return r
end
print(mymatch(g, "one,two"))
print(mymatch(g, "one two"))
print(mymatch(g, "one,\n two,\nthree,"))

View file

@ -0,0 +1,39 @@
local m = require'lpeglabel'
local re = require'relabel'
local terror = {}
local function newError(s)
table.insert(terror, s)
return #terror
end
local errUndef = newError("undefined")
local errId = newError("expecting an identifier")
local errComma = newError("expecting ','")
local id = m.R'az'^1
local g = m.P{
"S",
S = m.V"Id" * m.V"List",
List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
Id = m.V"Sp" * id + m.T(errId),
Comma = m.V"Sp" * "," + m.T(errComma),
Sp = m.S" \n\t"^0,
}
function mymatch (g, s)
local r, e, sfail = g:match(s)
if not r then
local line, col = re.calcline(s, #s - #sfail)
local msg = "Error at line " .. line .. " (col " .. col .. "): "
return r, msg .. terror[e] .. " before '" .. sfail .. "'"
end
return r
end
print(mymatch(g, "one,two"))
print(mymatch(g, "one two"))
print(mymatch(g, "one,\n two,\nthree,"))

View file

@ -0,0 +1,67 @@
local m = require'lpeglabel'
local re = require'relabel'
local terror = {}
local function newError(s)
table.insert(terror, s)
return #terror
end
local errUndef = newError("undefined")
local errId = newError("expecting an identifier")
local errComma = newError("expecting ','")
local id = m.R'az'^1
local g = m.P{
"S",
S = m.V"Id" * m.V"List",
List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
Id = m.V"Sp" * id + m.T(errId),
Comma = m.V"Sp" * "," + m.T(errComma),
Sp = m.S" \n\t"^0,
}
local subject, errors
function recorderror(pos, lab)
local line, col = re.calcline(subject, pos)
table.insert(errors, { line = line, col = col, msg = terror[lab] })
end
function record (lab)
return (m.Cp() * m.Cc(lab)) / recorderror
end
function sync (p)
return (-p * m.P(1))^0
end
local grec = m.P{
"S",
S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId),
ErrComma = record(errComma) * sync(id),
ErrId = record(errId) * sync(m.P",")
}
function mymatch (g, s)
errors = {}
subject = s
local r, e, sfail = g:match(s)
if #errors > 0 then
local out = {}
for i, err in ipairs(errors) do
local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
table.insert(out, msg)
end
return nil, table.concat(out, "\n") .. "\n"
end
return r
end
print(mymatch(grec, "one,two"))
print(mymatch(grec, "one two three"))
print(mymatch(grec, "1,\n two, \n3,"))
print(mymatch(grec, "one\n two123, \nthree,"))

View file

@ -0,0 +1,79 @@
local m = require'lpeglabel'
local re = require'relabel'
local terror = {}
local function newError(s)
table.insert(terror, s)
return #terror
end
local errUndef = newError("undefined")
local errId = newError("expecting an identifier")
local errComma = newError("expecting ','")
local id = m.R'az'^1
local g = m.P{
"S",
S = m.V"Id" * m.V"List",
List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
Id = m.V"Sp" * m.C(id) + m.T(errId),
Comma = m.V"Sp" * "," + m.T(errComma),
Sp = m.S" \n\t"^0,
}
local subject, errors
function recorderror(pos, lab)
local line, col = re.calcline(subject, pos)
table.insert(errors, { line = line, col = col, msg = terror[lab] })
end
function record (lab)
return (m.Cp() * m.Cc(lab)) / recorderror
end
function sync (p)
return (-p * m.P(1))^0
end
function defaultValue ()
return m.Cc"NONE"
end
local grec = m.P{
"S",
S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId),
ErrComma = record(errComma) * sync(id),
ErrId = record(errId) * sync(m.P",") * defaultValue(),
}
function mymatch (g, s)
errors = {}
subject = s
io.write("Input: ", s, "\n")
local r = { g:match(s) }
io.write("Captures (separated by ';'): ")
for k, v in pairs(r) do
io.write(v .. "; ")
end
io.write("\nSyntactic errors found: " .. #errors)
if #errors > 0 then
io.write("\n")
local out = {}
for i, err in ipairs(errors) do
local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
table.insert(out, msg)
end
io.write(table.concat(out, "\n"))
end
print("\n")
return r
end
mymatch(grec, "one,two")
mymatch(grec, "one two three")
mymatch(grec, "1,\n two, \n3,")
mymatch(grec, "one\n two123, \nthree,")

View file

@ -0,0 +1,30 @@
local re = require 'relabel'
local g = re.compile[[
S <- Id List
List <- !. / Comma Id List
Id <- Sp [a-z]+ / %{2}
Comma <- Sp ',' / %{3}
Sp <- %s*
]]
function mymatch (g, s)
local r, e, sfail = g:match(s)
if not r then
local line, col = re.calcline(s, #s - #sfail)
local msg = "Error at line " .. line .. " (col " .. col .. ")"
if e == 1 then
return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
elseif e == 2 then
return r, msg .. ": expecting ',' before '" .. sfail .. "'"
else
return r, msg
end
end
return r
end
print(mymatch(g, "one,two"))
print(mymatch(g, "one two"))
print(mymatch(g, "one,\n two,\nthree,"))

View file

@ -0,0 +1,71 @@
local re = require 'relabel'
local errinfo = {
{"errUndef", "undefined"},
{"errId", "expecting an identifier"},
{"errComma", "expecting ','"},
}
local errmsgs = {}
local labels = {}
for i, err in ipairs(errinfo) do
errmsgs[i] = err[2]
labels[err[1]] = i
end
re.setlabels(labels)
local g = re.compile[[
S <- Id List
List <- !. / Comma Id List
Id <- Sp {[a-z]+} / %{errId}
Comma <- Sp ',' / %{errComma}
Sp <- %s*
]]
local errors
function recorderror (subject, pos, label)
local line, col = re.calcline(subject, pos)
table.insert(errors, { line = line, col = col, msg = errmsgs[labels[label]] })
return true
end
function sync (p)
return '( !(' .. p .. ') .)*'
end
local grec = re.compile(
"S <- %g //{errComma} ErrComma //{errId} ErrId" .. "\n" ..
"ErrComma <- ('' -> 'errComma' => recorderror) " .. sync('[a-z]+') .. "\n" ..
"ErrId <- ('' -> 'errId' => recorderror) " .. sync('","') .. "-> default"
, {g = g, recorderror = recorderror, default = "NONE"})
function mymatch (g, s)
errors = {}
subject = s
io.write("Input: ", s, "\n")
local r = { g:match(s) }
io.write("Captures (separated by ';'): ")
for k, v in pairs(r) do
io.write(v .. "; ")
end
io.write("\nSyntactic errors found: " .. #errors)
if #errors > 0 then
io.write("\n")
local out = {}
for i, err in ipairs(errors) do
local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
table.insert(out, msg)
end
io.write(table.concat(out, "\n"))
end
print("\n")
return r
end
mymatch(grec, "one,two")
mymatch(grec, "one two three")
mymatch(grec, "1,\n two, \n3,")
mymatch(grec, "one\n two123, \nthree,")

View file

@ -0,0 +1,151 @@
local re = require 'relabel'
local terror = {}
local function newError(l, msg)
table.insert(terror, { l = l, msg = msg} )
end
newError("errSemi", "Error: missing ';'")
newError("errExpIf", "Error: expected expression after 'if'")
newError("errThen", "Error: expected 'then' keyword")
newError("errCmdSeq1", "Error: expected at least a command after 'then'")
newError("errCmdSeq2", "Error: expected at least a command after 'else'")
newError("errEnd", "Error: expected 'end' keyword")
newError("errCmdSeqRep", "Error: expected at least a command after 'repeat'")
newError("errUntil", "Error: expected 'until' keyword")
newError("errExpRep", "Error: expected expression after 'until'")
newError("errAssignOp", "Error: expected ':=' in assigment")
newError("errExpAssign", "Error: expected expression after ':='")
newError("errReadName", "Error: expected an identifier after 'read'")
newError("errWriteExp", "Error: expected expression after 'write'")
newError("errSimpExp", "Error: expected '(', ID, or number after '<' or '='")
newError("errTerm", "Error: expected '(', ID, or number after '+' or '-'")
newError("errFactor", "Error: expected '(', ID, or number after '*' or '/'")
newError("errExpFac", "Error: expected expression after '('")
newError("errClosePar", "Error: expected ')' after expression")
local labelCode = {}
for k, v in ipairs(terror) do
labelCode[v.l] = k
end
re.setlabels(labelCode)
local g = re.compile[[
Tiny <- CmdSeq
CmdSeq <- (Cmd (SEMICOLON / ErrSemi)) (Cmd (SEMICOLON / ErrSemi))*
Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd
IfCmd <- IF (Exp / ErrExpIf) (THEN / ErrThen) (CmdSeq / ErrCmdSeq1) (ELSE (CmdSeq / ErrCmdSeq2) / '') (END / ErrEnd)
RepeatCmd <- REPEAT (CmdSeq / ErrCmdSeqRep) (UNTIL / ErrUntil) (Exp / ErrExpRep)
AssignCmd <- NAME (ASSIGNMENT / ErrAssignOp) (Exp / ErrExpAssign)
ReadCmd <- READ (NAME / ErrReadName)
WriteCmd <- WRITE (Exp / ErrWriteExp)
Exp <- SimpleExp ((LESS / EQUAL) (SimpleExp / ErrSimpExp) / '')
SimpleExp <- Term ((ADD / SUB) (Term / ErrTerm))*
Term <- Factor ((MUL / DIV) (Factor / ErrFactor))*
Factor <- OPENPAR (Exp / ErrExpFac) (CLOSEPAR / ErrClosePar) / NUMBER / NAME
ErrSemi <- %{errSemi}
ErrExpIf <- %{errExpIf}
ErrThen <- %{errThen}
ErrCmdSeq1 <- %{errCmdSeq1}
ErrCmdSeq2 <- %{errCmdSeq2}
ErrEnd <- %{errEnd}
ErrCmdSeqRep <- %{errCmdSeqRep}
ErrUntil <- %{errUntil}
ErrExpRep <- %{errExpRep}
ErrAssignOp <- %{errAssignOp}
ErrExpAssign <- %{errExpAssign}
ErrReadName <- %{errReadName}
ErrWriteExp <- %{errWriteExp}
ErrSimpExp <- %{errSimpExp}
ErrTerm <- %{errTerm}
ErrFactor <- %{errFactor}
ErrExpFac <- %{errExpFac}
ErrClosePar <- %{errClosePar}
ADD <- Sp '+'
ASSIGNMENT <- Sp ':='
CLOSEPAR <- Sp ')'
DIV <- Sp '/'
IF <- Sp 'if'
ELSE <- Sp 'else'
END <- Sp 'end'
EQUAL <- Sp '='
LESS <- Sp '<'
MUL <- Sp '*'
NAME <- Sp !RESERVED [a-z]+
NUMBER <- Sp [0-9]+
OPENPAR <- Sp '('
READ <- Sp 'read'
REPEAT <- Sp 'repeat'
SEMICOLON <- Sp ';'
SUB <- Sp '-'
THEN <- Sp 'then'
UNTIL <- Sp 'until'
WRITE <- Sp 'write'
RESERVED <- (IF / ELSE / END / READ / REPEAT / THEN / UNTIL / WRITE) ![a-z]+
Sp <- %s*
]]
local function mymatch(g, s)
local r, e, sfail = g:match(s)
if not r then
local line, col = re.calcline(s, #s - #sfail)
local msg = "Error at line " .. line .. " (col " .. col .. "): "
return r, msg .. terror[e].msg
end
return r
end
local s = [[
n := 5;
f := 1;
repeat
f := f + n;
n := n - 1
until (n < 1);
write f;]]
print(mymatch(g, s))
s = [[
n := 5;
f := 1;
repeat
f := f + n;
n := n - 1;
until (n < 1);
read ;]]
print(mymatch(g, s))
s = [[
if a < 1 then
b := 2;
else
b := 3;]]
print(mymatch(g, s))
s = [[
n := 5;
f := 1;
repeat
f := f + n;
n := n - 1;
untill (n < 1);
]]
print(mymatch(g, s))
s = [[
n := 5;
f := 1;
repeat
f := f + n;
n := n - 1;
3 (n < 1);
]]
print(mymatch(g, s))
print(mymatch(g, "a : 2"))
print(mymatch(g, "a := (2"))

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,66 @@
local errors = {}
local function new_error (label, msg)
table.insert(errors, { label = label, msg = msg })
end
new_error("Number", "malformed <number>")
new_error("String", "malformed <string>")
new_error("LongString", "unfinished long string")
new_error("LongComment", "unfinished long comment")
new_error("MissingOP", "missing '('")
new_error("MissingCP", "missing ')'")
new_error("MissingCC", "missing '}'")
new_error("MissingCB", "missing ']'")
new_error("UnionType", "expecting <type> after '|'")
new_error("FunctionType", "expecting <type> after '->'")
new_error("MethodType", "expecting <type> after '=>'")
new_error("TupleType", "expecting <type> after ','")
new_error("Type", "expecting <type> after ':'")
new_error("TypeDecEnd", "missing 'end' in type declaration")
new_error("TypeAliasName", "expecting <name> after 'typealias'")
new_error("MissingEqTypeAlias", "missing '=' in 'typealias'")
new_error("DotIndex", "expecting <name> after '.'")
new_error("MethodName", "expecting <name> after ':'")
new_error("Then", "missing 'then'")
new_error("IfEnd", "missing 'end' to close if statement")
new_error("WhileDo", "missing 'do' in while statement")
new_error("WhileEnd", "missing 'end' to close while statement")
new_error("BlockEnd", "missing 'end' to close block")
new_error("ForDo", "missing 'do' in for statement")
new_error("ForEnd", "missing 'end' to close for statement")
new_error("Until", "missing 'until' in repeat statement")
new_error("FuncEnd", "missing 'end' to close function declaration")
new_error("ParList", "expecting '...'")
new_error("MethodCall", "expecting '(' for method call")
new_error("Label1", "expecting <name> after '::'")
new_error("Label2", "expecting '::' to close label declaration")
new_error("LocalAssign1", "expecting expression list after '='")
new_error("LocalAssign2", "invalid local declaration")
new_error("ForGen", "expecting 'in'")
new_error("LocalFunc", "expecting <name> in local function declaration")
new_error("RetStat", "invalid statement after 'return'")
new_error("ElseIf", "expecting <exp> after 'elseif'")
new_error("SubExpr_1", "malformed 'or' expression")
new_error("SubExpr_2", "malformed 'and' expression")
new_error("SubExpr_3", "malformed relational expression")
new_error("SubExpr_4", "malformed '|' expression")
new_error("SubExpr_5", "malformed '~' expression")
new_error("SubExpr_6", "malformed '&' expression")
new_error("SubExpr_7", "malformed shift expression")
new_error("SubExpr_8", "malformed '..' expression")
new_error("SubExpr_9", "malformed addition expression")
new_error("SubExpr_10", "malformed multiplication expression")
new_error("SubExpr_11", "malformed unary expression")
new_error("SubExpr_12", "malformed '^' expression")
new_error("Stat", "invalid statement")
local labels = {}
for k, v in ipairs(errors) do
labels[v.label] = k
end
return {
errors = errors,
labels = labels,
}

View file

@ -0,0 +1,105 @@
local tllexer = {}
local lpeg = require "lpeglabel"
lpeg.locale(lpeg)
local tlerror = require "tlerror"
function tllexer.try (pat, label)
return pat + lpeg.T(tlerror.labels[label])
end
local function setffp (s, i, t, n)
if not t.ffp or i > t.ffp then
t.ffp = i
t.list = {}
t.list[n] = true
t.expected = "'" .. n .. "'"
elseif i == t.ffp then
if not t.list[n] then
t.list[n] = true
t.expected = "'" .. n .. "', " .. t.expected
end
end
return false
end
local function updateffp (name)
return lpeg.Cmt(lpeg.Carg(1) * lpeg.Cc(name), setffp)
end
tllexer.Shebang = lpeg.P("#") * (lpeg.P(1) - lpeg.P("\n"))^0 * lpeg.P("\n")
local Space = lpeg.space^1
local Equals = lpeg.P("=")^0
local Open = "[" * lpeg.Cg(Equals, "init") * "[" * lpeg.P("\n")^-1
local Close = "]" * lpeg.C(Equals) * "]"
local CloseEQ = lpeg.Cmt(Close * lpeg.Cb("init"),
function (s, i, a, b) return a == b end)
local LongString = Open * (lpeg.P(1) - CloseEQ)^0 * tllexer.try(Close, "LongString") /
function (s, o) return s end
local LongStringCm1 = Open * (lpeg.P(1) - CloseEQ)^0 * Close /
function (s, o) return s end
local Comment = lpeg.Rec(lpeg.P"--" * #Open * (LongStringCm1 / function() return end + lpeg.T(tlerror.labels["LongString"])),
lpeg.T(tlerror.labels["LongComment"]), tlerror.labels["LongString"]) +
lpeg.P("--") * (lpeg.P(1) - lpeg.P("\n"))^0
tllexer.Skip = (Space + Comment)^0
local idStart = lpeg.alpha + lpeg.P("_")
local idRest = lpeg.alnum + lpeg.P("_")
local Keywords = lpeg.P("and") + "break" + "do" + "elseif" + "else" + "end" +
"false" + "for" + "function" + "goto" + "if" + "in" +
"local" + "nil" + "not" + "or" + "repeat" + "return" +
"then" + "true" + "until" + "while"
tllexer.Reserved = Keywords * -idRest
local Identifier = idStart * idRest^0
tllexer.Name = -tllexer.Reserved * Identifier * -idRest
function tllexer.token (pat, name)
return pat * tllexer.Skip + updateffp(name) * lpeg.P(false)
end
function tllexer.symb (str)
return tllexer.token(lpeg.P(str), str)
end
function tllexer.kw (str)
return tllexer.token(lpeg.P(str) * -idRest, str)
end
local Hex = (lpeg.P("0x") + lpeg.P("0X")) * tllexer.try(lpeg.xdigit^1, "Number")
local Expo = lpeg.S("eE") * lpeg.S("+-")^-1 * tllexer.try(lpeg.digit^1, "Number")
local Float = (((lpeg.digit^1 * lpeg.P(".") * lpeg.digit^0 * tllexer.try(-lpeg.P("."), "Number")) +
(lpeg.P(".") * lpeg.digit^1)) * Expo^-1) +
(lpeg.digit^1 * Expo)
local Int = lpeg.digit^1
tllexer.Number = Hex + Float + Int
local ShortString = lpeg.P('"') *
((lpeg.P('\\') * lpeg.P(1)) + (lpeg.P(1) - lpeg.P('"')))^0 *
tllexer.try(lpeg.P('"'), "String") +
lpeg.P("'") *
((lpeg.P("\\") * lpeg.P(1)) + (lpeg.P(1) - lpeg.P("'")))^0 *
tllexer.try(lpeg.P("'"), "String")
tllexer.String = LongString + ShortString
-- for error reporting
tllexer.OneWord = tllexer.Name +
tllexer.Number +
tllexer.String +
tllexer.Reserved +
lpeg.P("...") +
lpeg.P(1)
return tllexer

View file

@ -0,0 +1,20 @@
local tlparser = require "tlparser"
local function getcontents(filename)
file = assert(io.open(filename, "r"))
contents = file:read("*a")
file:close()
return contents
end
if #arg ~= 1 then
print ("Usage: lua tlp.lua <file>")
os.exit(1)
end
local filename = arg[1]
local subject = getcontents(filename)
local r, msg = tlparser.parse(subject, filename, false, true)
if not r then print(msg) end
os.exit(0)

View file

@ -0,0 +1,245 @@
local tlparser = {}
local lpeg = require "lpeglabel"
lpeg.locale(lpeg)
local tllexer = require "tllexer"
local tlerror = require "tlerror"
local function chainl1 (pat, sep, label)
return pat * (sep * tllexer.try(pat, label))^0
end
local G = lpeg.P { "TypedLua";
TypedLua = tllexer.Shebang^-1 * tllexer.Skip * lpeg.V("Chunk") * tllexer.try(-1, "Stat");
-- type language
Type = lpeg.V("NilableType");
NilableType = lpeg.V("UnionType") * tllexer.symb("?")^-1;
UnionType = lpeg.V("PrimaryType") * (tllexer.symb("|") * tllexer.try(lpeg.V("PrimaryType"), "UnionType"))^0;
PrimaryType = lpeg.V("LiteralType") +
lpeg.V("BaseType") +
lpeg.V("NilType") +
lpeg.V("ValueType") +
lpeg.V("AnyType") +
lpeg.V("SelfType") +
lpeg.V("FunctionType") +
lpeg.V("TableType") +
lpeg.V("VariableType");
LiteralType = tllexer.token("false", "false") +
tllexer.token("true", "true") +
tllexer.token(tllexer.Number, "Number") +
tllexer.token(tllexer.String, "String");
BaseType = tllexer.token("boolean", "boolean") +
tllexer.token("number", "number") +
tllexer.token("string", "string") +
tllexer.token("integer", "integer");
NilType = tllexer.token("nil", "nil");
ValueType = tllexer.token("value", "value");
AnyType = tllexer.token("any", "any");
SelfType = tllexer.token("self", "self");
FunctionType = lpeg.V("InputType") * tllexer.symb("->") * tllexer.try(lpeg.V("NilableTuple"), "FunctionType");
MethodType = lpeg.V("InputType") * tllexer.symb("=>") * tllexer.try(lpeg.V("NilableTuple"), "MethodType");
InputType = tllexer.symb("(") * lpeg.V("TupleType")^-1 * tllexer.try(tllexer.symb(")"), "MissingCP");
NilableTuple = lpeg.V("UnionlistType") * tllexer.symb("?")^-1;
UnionlistType = lpeg.V("OutputType") * (tllexer.symb("|") * tllexer.try(lpeg.V("OutputType"), "UnionType"))^0;
OutputType = tllexer.symb("(") * lpeg.V("TupleType")^-1 * tllexer.try(tllexer.symb(")"), "MissingCP");
TupleType = lpeg.V("Type") * (tllexer.symb(",") * tllexer.try(lpeg.V("Type"), "TupleType"))^0 * tllexer.symb("*")^-1;
TableType = tllexer.symb("{") * lpeg.V("TableTypeBody")^-1 * tllexer.try(tllexer.symb("}"), "MissingCC");
TableTypeBody = lpeg.V("RecordType") +
lpeg.V("HashType") +
lpeg.V("ArrayType");
RecordType = lpeg.V("RecordField") * (tllexer.symb(",") * lpeg.V("RecordField"))^0 *
(tllexer.symb(",") * (lpeg.V("HashType") + lpeg.V("ArrayType")))^-1;
RecordField = tllexer.kw("const")^-1 *
lpeg.V("LiteralType") * tllexer.symb(":") * tllexer.try(lpeg.V("Type"), "Type");
HashType = lpeg.V("KeyType") * tllexer.symb(":") * tllexer.try(lpeg.V("FieldType"), "Type");
ArrayType = lpeg.V("FieldType");
KeyType = lpeg.V("BaseType") + lpeg.V("ValueType") + lpeg.V("AnyType");
FieldType = lpeg.V("Type");
VariableType = tllexer.token(tllexer.Name, "Name");
RetType = lpeg.V("NilableTuple") +
lpeg.V("Type");
Id = tllexer.token(tllexer.Name, "Name");
TypeDecId = (tllexer.kw("const") * lpeg.V("Id")) +
lpeg.V("Id");
IdList = lpeg.V("TypeDecId") * (tllexer.symb(",") * tllexer.try(lpeg.V("TypeDecId"), "TupleType"))^0;
IdDec = lpeg.V("IdList") * tllexer.symb(":") * tllexer.try((lpeg.V("Type") + lpeg.V("MethodType")), "Type");
IdDecList = (lpeg.V("IdDec")^1)^-1;
TypeDec = tllexer.token(tllexer.Name, "Name") * lpeg.V("IdDecList") * tllexer.try(tllexer.kw("end"), "TypeDecEnd");
Interface = tllexer.kw("interface") * lpeg.V("TypeDec") +
tllexer.kw("typealias") *
tllexer.try(tllexer.token(tllexer.Name, "Name"), "TypeAliasName") *
tllexer.try(tllexer.symb("="), "MissingEqTypeAlias") * lpeg.V("Type");
-- parser
Chunk = lpeg.V("Block");
StatList = (tllexer.symb(";") + lpeg.V("Stat"))^0;
Var = lpeg.V("Id");
TypedId = tllexer.token(tllexer.Name, "Name") * (tllexer.symb(":") * tllexer.try(lpeg.V("Type"), "Type"))^-1;
FunctionDef = tllexer.kw("function") * lpeg.V("FuncBody");
FieldSep = tllexer.symb(",") + tllexer.symb(";");
Field = ((tllexer.symb("[") * lpeg.V("Expr") * tllexer.try(tllexer.symb("]"), "MissingCB")) +
(tllexer.token(tllexer.Name, "Name"))) *
tllexer.symb("=") * lpeg.V("Expr") +
lpeg.V("Expr");
TField = (tllexer.kw("const") * lpeg.V("Field")) +
lpeg.V("Field");
FieldList = (lpeg.V("TField") * (lpeg.V("FieldSep") * lpeg.V("TField"))^0 *
lpeg.V("FieldSep")^-1)^-1;
Constructor = tllexer.symb("{") * lpeg.V("FieldList") * tllexer.try(tllexer.symb("}"), "MissingCC");
NameList = lpeg.V("TypedId") * (tllexer.symb(",") * lpeg.V("TypedId"))^0;
ExpList = lpeg.V("Expr") * (tllexer.symb(",") * lpeg.V("Expr"))^0;
FuncArgs = tllexer.symb("(") *
(lpeg.V("Expr") * (tllexer.symb(",") * lpeg.V("Expr"))^0)^-1 *
tllexer.try(tllexer.symb(")"), "MissingCP") +
lpeg.V("Constructor") +
tllexer.token(tllexer.String, "String");
OrOp = tllexer.kw("or");
AndOp = tllexer.kw("and");
RelOp = tllexer.symb("~=") +
tllexer.symb("==") +
tllexer.symb("<=") +
tllexer.symb(">=") +
tllexer.symb("<") +
tllexer.symb(">");
BOrOp = tllexer.symb("|");
BXorOp = tllexer.symb("~") * -lpeg.P("=");
BAndOp = tllexer.symb("&");
ShiftOp = tllexer.symb("<<") +
tllexer.symb(">>");
ConOp = tllexer.symb("..");
AddOp = tllexer.symb("+") +
tllexer.symb("-");
MulOp = tllexer.symb("*") +
tllexer.symb("//") +
tllexer.symb("/") +
tllexer.symb("%");
UnOp = tllexer.kw("not") +
tllexer.symb("-") +
tllexer.symb("~") +
tllexer.symb("#");
PowOp = tllexer.symb("^");
Expr = lpeg.V("SubExpr_1");
SubExpr_1 = chainl1(lpeg.V("SubExpr_2"), lpeg.V("OrOp"), "SubExpr_1");
SubExpr_2 = chainl1(lpeg.V("SubExpr_3"), lpeg.V("AndOp"), "SubExpr_2");
SubExpr_3 = chainl1(lpeg.V("SubExpr_4"), lpeg.V("RelOp"), "SubExpr_3");
SubExpr_4 = chainl1(lpeg.V("SubExpr_5"), lpeg.V("BOrOp"), "SubExpr_4");
SubExpr_5 = chainl1(lpeg.V("SubExpr_6"), lpeg.V("BXorOp"), "SubExpr_5");
SubExpr_6 = chainl1(lpeg.V("SubExpr_7"), lpeg.V("BAndOp"), "SubExpr_6");
SubExpr_7 = chainl1(lpeg.V("SubExpr_8"), lpeg.V("ShiftOp"), "SubExpr_7");
SubExpr_8 = lpeg.V("SubExpr_9") * lpeg.V("ConOp") * tllexer.try(lpeg.V("SubExpr_8"), "SubExpr_8") +
lpeg.V("SubExpr_9");
SubExpr_9 = chainl1(lpeg.V("SubExpr_10"), lpeg.V("AddOp"), "SubExpr_9");
SubExpr_10 = chainl1(lpeg.V("SubExpr_11"), lpeg.V("MulOp"), "SubExpr_10");
SubExpr_11 = lpeg.V("UnOp") * tllexer.try(lpeg.V("SubExpr_11"), "SubExpr_11") +
lpeg.V("SubExpr_12");
SubExpr_12 = lpeg.V("SimpleExp") * (lpeg.V("PowOp") * tllexer.try(lpeg.V("SubExpr_11"), "SubExpr_12"))^-1;
SimpleExp = tllexer.token(tllexer.Number, "Number") +
tllexer.token(tllexer.String, "String") +
tllexer.kw("nil") +
tllexer.kw("false") +
tllexer.kw("true") +
tllexer.symb("...") +
lpeg.V("FunctionDef") +
lpeg.V("Constructor") +
lpeg.V("SuffixedExp");
SuffixedExp = lpeg.V("PrimaryExp") * (
(tllexer.symb(".") * tllexer.try(tllexer.token(tllexer.Name, "Name"), "DotIndex")) / "index" +
(tllexer.symb("[") * lpeg.V("Expr") * tllexer.try(tllexer.symb("]"), "MissingCB")) / "index" +
(tllexer.symb(":") * tllexer.try(tllexer.token(tllexer.Name, "Name"), "MethodName") * tllexer.try(lpeg.V("FuncArgs"), "MethodCall")) / "call" +
lpeg.V("FuncArgs") / "call")^0 / function (...) local l = {...}; return l[#l] end;
PrimaryExp = lpeg.V("Var") / "var" +
tllexer.symb("(") * lpeg.V("Expr") * tllexer.try(tllexer.symb(")"), "MissingCP");
Block = lpeg.V("StatList") * lpeg.V("RetStat")^-1;
IfStat = tllexer.kw("if") * lpeg.V("Expr") * tllexer.try(tllexer.kw("then"), "Then") * lpeg.V("Block") *
(tllexer.kw("elseif") * tllexer.try(lpeg.V("Expr"), "ElseIf") * tllexer.try(tllexer.kw("then"), "Then") * lpeg.V("Block"))^0 *
(tllexer.kw("else") * lpeg.V("Block"))^-1 *
tllexer.try(tllexer.kw("end"), "IfEnd");
WhileStat = tllexer.kw("while") * lpeg.V("Expr") *
tllexer.try(tllexer.kw("do"), "WhileDo") * lpeg.V("Block") * tllexer.try(tllexer.kw("end"), "WhileEnd");
DoStat = tllexer.kw("do") * lpeg.V("Block") * tllexer.try(tllexer.kw("end"), "BlockEnd");
ForBody = tllexer.try(tllexer.kw("do"), "ForDo") * lpeg.V("Block");
ForNum = lpeg.V("Id") * tllexer.symb("=") * lpeg.V("Expr") * tllexer.symb(",") *
lpeg.V("Expr") * (tllexer.symb(",") * lpeg.V("Expr"))^-1 *
lpeg.V("ForBody");
ForGen = lpeg.V("NameList") * tllexer.try(tllexer.kw("in"), "ForGen") *
lpeg.V("ExpList") * lpeg.V("ForBody");
ForStat = tllexer.kw("for") * (lpeg.V("ForNum") + lpeg.V("ForGen")) * tllexer.try(tllexer.kw("end"), "ForEnd");
RepeatStat = tllexer.kw("repeat") * lpeg.V("Block") *
tllexer.try(tllexer.kw("until"), "Until") * lpeg.V("Expr");
FuncName = lpeg.V("Id") * (tllexer.symb(".") *
(tllexer.token(tllexer.Name, "Name")))^0 *
(tllexer.symb(":") * (tllexer.token(tllexer.Name, "Name")))^-1;
ParList = lpeg.V("NameList") * (tllexer.symb(",") * tllexer.try(lpeg.V("TypedVarArg"), "ParList"))^-1 +
lpeg.V("TypedVarArg");
TypedVarArg = tllexer.symb("...") * (tllexer.symb(":") * tllexer.try(lpeg.V("Type"), "Type"))^-1;
FuncBody = tllexer.try(tllexer.symb("("), "MissingOP") * lpeg.V("ParList")^-1 * tllexer.try(tllexer.symb(")"), "MissingCP") *
(tllexer.symb(":") * tllexer.try(lpeg.V("RetType"), "Type"))^-1 *
lpeg.V("Block") * tllexer.try(tllexer.kw("end"), "FuncEnd");
FuncStat = tllexer.kw("const")^-1 *
tllexer.kw("function") * lpeg.V("FuncName") * lpeg.V("FuncBody");
LocalFunc = tllexer.kw("function") *
tllexer.try(lpeg.V("Id"), "LocalFunc") * lpeg.V("FuncBody");
LocalAssign = lpeg.V("NameList") * tllexer.symb("=") * tllexer.try(lpeg.V("ExpList"), "LocalAssign1") +
lpeg.V("NameList") * (#(-tllexer.symb("=") * (lpeg.V("Stat") + -1)) * lpeg.P(true)) + lpeg.T(tlerror.labels["LocalAssign2"]);
LocalStat = tllexer.kw("local") *
(lpeg.V("LocalTypeDec") + lpeg.V("LocalFunc") + lpeg.V("LocalAssign"));
LabelStat = tllexer.symb("::") * tllexer.try(tllexer.token(tllexer.Name, "Name"), "Label1") * tllexer.try(tllexer.symb("::"), "Label2");
BreakStat = tllexer.kw("break");
GoToStat = tllexer.kw("goto") * tllexer.token(tllexer.Name, "Name");
RetStat = tllexer.kw("return") * tllexer.try(-lpeg.V("Stat"), "RetStat") *
(lpeg.V("Expr") * (tllexer.symb(",") * lpeg.V("Expr"))^0)^-1 *
tllexer.symb(";")^-1;
TypeDecStat = lpeg.V("Interface");
LocalTypeDec = lpeg.V("TypeDecStat");
LVar = (tllexer.kw("const") * lpeg.V("SuffixedExp")) +
lpeg.V("SuffixedExp");
ExprStat = lpeg.Cmt(lpeg.V("LVar") * lpeg.V("Assignment"),
function (s, i, ...)
local l = {...}
local i = 1
while l[i] ~= "=" do
local se = l[i]
if se ~= "var" and se ~= "index" then return false end
i = i + 1
end
return true
end) +
lpeg.Cmt(lpeg.V("SuffixedExp"),
function (s, i, se)
if se ~= "call" then return false end
return true
end);
Assignment = ((tllexer.symb(",") * lpeg.V("LVar"))^1)^-1 * (tllexer.symb("=") / "=") * lpeg.V("ExpList");
Stat = lpeg.V("IfStat") + lpeg.V("WhileStat") + lpeg.V("DoStat") + lpeg.V("ForStat") +
lpeg.V("RepeatStat") + lpeg.V("FuncStat") + lpeg.V("LocalStat") +
lpeg.V("LabelStat") + lpeg.V("BreakStat") + lpeg.V("GoToStat") +
lpeg.V("TypeDecStat") + lpeg.V("ExprStat");
}
local function lineno (s, i)
if i == 1 then return 1, 1 end
local rest, num = s:sub(1,i):gsub("[^\n]*\n", "")
local r = #rest
return 1 + num, r ~= 0 and r or 1
end
function tlparser.parse (subject, filename, strict, integer)
local errorinfo = {}
lpeg.setmaxstack(1000)
local ast, label, suffix = lpeg.match(G, subject, nil, errorinfo, strict, integer)
if not ast then
local line, col = lineno(subject, string.len(subject) - string.len(suffix))
local error_msg = string.format("%s:%d:%d: ", filename, line, col)
if label ~= 0 then
error_msg = error_msg .. tlerror.errors[label].msg
else
local u = lpeg.match(lpeg.C(tllexer.OneWord) + lpeg.Cc("EOF"), subject, errorinfo.ffp)
error_msg = error_msg .. string.format("unexpected '%s', expecting %s", u, errorinfo.expected)
end
return nil, error_msg
else
return true
end
end
return tlparser

537
06/deps/lpeglabel/lpcap.c Normal file
View file

@ -0,0 +1,537 @@
/*
** $Id: lpcap.c,v 1.6 2015/06/15 16:09:57 roberto Exp $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/
#include "lua.h"
#include "lauxlib.h"
#include "lpcap.h"
#include "lptypes.h"
#define captype(cap) ((cap)->kind)
#define isclosecap(cap) (captype(cap) == Cclose)
#define closeaddr(c) ((c)->s + (c)->siz - 1)
#define isfullcap(cap) ((cap)->siz != 0)
#define getfromktable(cs,v) lua_rawgeti((cs)->L, ktableidx((cs)->ptop), v)
#define pushluaval(cs) getfromktable(cs, (cs)->cap->idx)
/*
** Put at the cache for Lua values the value indexed by 'v' in ktable
** of the running pattern (if it is not there yet); returns its index.
*/
static int updatecache (CapState *cs, int v) {
int idx = cs->ptop + 1; /* stack index of cache for Lua values */
if (v != cs->valuecached) { /* not there? */
getfromktable(cs, v); /* get value from 'ktable' */
lua_replace(cs->L, idx); /* put it at reserved stack position */
cs->valuecached = v; /* keep track of what is there */
}
return idx;
}
static int pushcapture (CapState *cs);
/*
** Goes back in a list of captures looking for an open capture
** corresponding to a close
*/
static Capture *findopen (Capture *cap) {
int n = 0; /* number of closes waiting an open */
for (;;) {
cap--;
if (isclosecap(cap)) n++; /* one more open to skip */
else if (!isfullcap(cap))
if (n-- == 0) return cap;
}
}
/*
** Go to the next capture
*/
static void nextcap (CapState *cs) {
Capture *cap = cs->cap;
if (!isfullcap(cap)) { /* not a single capture? */
int n = 0; /* number of opens waiting a close */
for (;;) { /* look for corresponding close */
cap++;
if (isclosecap(cap)) {
if (n-- == 0) break;
}
else if (!isfullcap(cap)) n++;
}
}
cs->cap = cap + 1; /* + 1 to skip last close (or entire single capture) */
}
/*
** Push on the Lua stack all values generated by nested captures inside
** the current capture. Returns number of values pushed. 'addextra'
** makes it push the entire match after all captured values. The
** entire match is pushed also if there are no other nested values,
** so the function never returns zero.
*/
static int pushnestedvalues (CapState *cs, int addextra) {
Capture *co = cs->cap;
if (isfullcap(cs->cap++)) { /* no nested captures? */
lua_pushlstring(cs->L, co->s, co->siz - 1); /* push whole match */
return 1; /* that is it */
}
else {
int n = 0;
while (!isclosecap(cs->cap)) /* repeat for all nested patterns */
n += pushcapture(cs);
if (addextra || n == 0) { /* need extra? */
lua_pushlstring(cs->L, co->s, cs->cap->s - co->s); /* push whole match */
n++;
}
cs->cap++; /* skip close entry */
return n;
}
}
/*
** Push only the first value generated by nested captures
*/
static void pushonenestedvalue (CapState *cs) {
int n = pushnestedvalues(cs, 0);
if (n > 1)
lua_pop(cs->L, n - 1); /* pop extra values */
}
/*
** Try to find a named group capture with the name given at the top of
** the stack; goes backward from 'cap'.
*/
static Capture *findback (CapState *cs, Capture *cap) {
lua_State *L = cs->L;
while (cap-- > cs->ocap) { /* repeat until end of list */
if (isclosecap(cap))
cap = findopen(cap); /* skip nested captures */
else if (!isfullcap(cap))
continue; /* opening an enclosing capture: skip and get previous */
if (captype(cap) == Cgroup) {
getfromktable(cs, cap->idx); /* get group name */
if (lp_equal(L, -2, -1)) { /* right group? */
lua_pop(L, 2); /* remove reference name and group name */
return cap;
}
else lua_pop(L, 1); /* remove group name */
}
}
luaL_error(L, "back reference '%s' not found", lua_tostring(L, -1));
return NULL; /* to avoid warnings */
}
/*
** Back-reference capture. Return number of values pushed.
*/
static int backrefcap (CapState *cs) {
int n;
Capture *curr = cs->cap;
pushluaval(cs); /* reference name */
cs->cap = findback(cs, curr); /* find corresponding group */
n = pushnestedvalues(cs, 0); /* push group's values */
cs->cap = curr + 1;
return n;
}
/*
** Table capture: creates a new table and populates it with nested
** captures.
*/
static int tablecap (CapState *cs) {
lua_State *L = cs->L;
int n = 0;
lua_newtable(L);
if (isfullcap(cs->cap++))
return 1; /* table is empty */
while (!isclosecap(cs->cap)) {
if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */
pushluaval(cs); /* push group name */
pushonenestedvalue(cs);
lua_settable(L, -3);
}
else { /* not a named group */
int i;
int k = pushcapture(cs);
for (i = k; i > 0; i--) /* store all values into table */
lua_rawseti(L, -(i + 1), n + i);
n += k;
}
}
cs->cap++; /* skip close entry */
return 1; /* number of values pushed (only the table) */
}
/*
** Table-query capture
*/
static int querycap (CapState *cs) {
int idx = cs->cap->idx;
pushonenestedvalue(cs); /* get nested capture */
lua_gettable(cs->L, updatecache(cs, idx)); /* query cap. value at table */
if (!lua_isnil(cs->L, -1))
return 1;
else { /* no value */
lua_pop(cs->L, 1); /* remove nil */
return 0;
}
}
/*
** Fold capture
*/
static int foldcap (CapState *cs) {
int n;
lua_State *L = cs->L;
int idx = cs->cap->idx;
if (isfullcap(cs->cap++) || /* no nested captures? */
isclosecap(cs->cap) || /* no nested captures (large subject)? */
(n = pushcapture(cs)) == 0) /* nested captures with no values? */
return luaL_error(L, "no initial value for fold capture");
if (n > 1)
lua_pop(L, n - 1); /* leave only one result for accumulator */
while (!isclosecap(cs->cap)) {
lua_pushvalue(L, updatecache(cs, idx)); /* get folding function */
lua_insert(L, -2); /* put it before accumulator */
n = pushcapture(cs); /* get next capture's values */
lua_call(L, n + 1, 1); /* call folding function */
}
cs->cap++; /* skip close entry */
return 1; /* only accumulator left on the stack */
}
/*
** Function capture
*/
static int functioncap (CapState *cs) {
int n;
int top = lua_gettop(cs->L);
pushluaval(cs); /* push function */
n = pushnestedvalues(cs, 0); /* push nested captures */
lua_call(cs->L, n, LUA_MULTRET); /* call function */
return lua_gettop(cs->L) - top; /* return function's results */
}
/*
** Select capture
*/
static int numcap (CapState *cs) {
int idx = cs->cap->idx; /* value to select */
if (idx == 0) { /* no values? */
nextcap(cs); /* skip entire capture */
return 0; /* no value produced */
}
else {
int n = pushnestedvalues(cs, 0);
if (n < idx) /* invalid index? */
return luaL_error(cs->L, "no capture '%d'", idx);
else {
lua_pushvalue(cs->L, -(n - idx + 1)); /* get selected capture */
lua_replace(cs->L, -(n + 1)); /* put it in place of 1st capture */
lua_pop(cs->L, n - 1); /* remove other captures */
return 1;
}
}
}
/*
** Return the stack index of the first runtime capture in the given
** list of captures (or zero if no runtime captures)
*/
int finddyncap (Capture *cap, Capture *last) {
for (; cap < last; cap++) {
if (cap->kind == Cruntime)
return cap->idx; /* stack position of first capture */
}
return 0; /* no dynamic captures in this segment */
}
/*
** Calls a runtime capture. Returns number of captures removed by
** the call, including the initial Cgroup. (Captures to be added are
** on the Lua stack.)
*/
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) {
int n, id;
lua_State *L = cs->L;
int otop = lua_gettop(L);
Capture *open = findopen(close);
assert(captype(open) == Cgroup);
id = finddyncap(open, close); /* get first dynamic capture argument */
close->kind = Cclose; /* closes the group */
close->s = s;
cs->cap = open; cs->valuecached = 0; /* prepare capture state */
luaL_checkstack(L, 4, "too many runtime captures");
pushluaval(cs); /* push function to be called */
lua_pushvalue(L, SUBJIDX); /* push original subject */
lua_pushinteger(L, s - cs->s + 1); /* push current position */
n = pushnestedvalues(cs, 0); /* push nested captures */
lua_call(L, n + 2, LUA_MULTRET); /* call dynamic function */
if (id > 0) { /* are there old dynamic captures to be removed? */
int i;
for (i = id; i <= otop; i++)
lua_remove(L, id); /* remove old dynamic captures */
*rem = otop - id + 1; /* total number of dynamic captures removed */
}
else
*rem = 0; /* no dynamic captures removed */
return close - open; /* number of captures of all kinds removed */
}
/*
** Auxiliary structure for substitution and string captures: keep
** information about nested captures for future use, avoiding to push
** string results into Lua
*/
typedef struct StrAux {
int isstring; /* whether capture is a string */
union {
Capture *cp; /* if not a string, respective capture */
struct { /* if it is a string... */
const char *s; /* ... starts here */
const char *e; /* ... ends here */
} s;
} u;
} StrAux;
#define MAXSTRCAPS 10
/*
** Collect values from current capture into array 'cps'. Current
** capture must be Cstring (first call) or Csimple (recursive calls).
** (In first call, fills %0 with whole match for Cstring.)
** Returns number of elements in the array that were filled.
*/
static int getstrcaps (CapState *cs, StrAux *cps, int n) {
int k = n++;
cps[k].isstring = 1; /* get string value */
cps[k].u.s.s = cs->cap->s; /* starts here */
if (!isfullcap(cs->cap++)) { /* nested captures? */
while (!isclosecap(cs->cap)) { /* traverse them */
if (n >= MAXSTRCAPS) /* too many captures? */
nextcap(cs); /* skip extra captures (will not need them) */
else if (captype(cs->cap) == Csimple) /* string? */
n = getstrcaps(cs, cps, n); /* put info. into array */
else {
cps[n].isstring = 0; /* not a string */
cps[n].u.cp = cs->cap; /* keep original capture */
nextcap(cs);
n++;
}
}
cs->cap++; /* skip close */
}
cps[k].u.s.e = closeaddr(cs->cap - 1); /* ends here */
return n;
}
/*
** add next capture value (which should be a string) to buffer 'b'
*/
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what);
/*
** String capture: add result to buffer 'b' (instead of pushing
** it into the stack)
*/
static void stringcap (luaL_Buffer *b, CapState *cs) {
StrAux cps[MAXSTRCAPS];
int n;
size_t len, i;
const char *fmt; /* format string */
fmt = lua_tolstring(cs->L, updatecache(cs, cs->cap->idx), &len);
n = getstrcaps(cs, cps, 0) - 1; /* collect nested captures */
for (i = 0; i < len; i++) { /* traverse them */
if (fmt[i] != '%') /* not an escape? */
luaL_addchar(b, fmt[i]); /* add it to buffer */
else if (fmt[++i] < '0' || fmt[i] > '9') /* not followed by a digit? */
luaL_addchar(b, fmt[i]); /* add to buffer */
else {
int l = fmt[i] - '0'; /* capture index */
if (l > n)
luaL_error(cs->L, "invalid capture index (%d)", l);
else if (cps[l].isstring)
luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s);
else {
Capture *curr = cs->cap;
cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */
if (!addonestring(b, cs, "capture"))
luaL_error(cs->L, "no values in capture index %d", l);
cs->cap = curr; /* continue from where it stopped */
}
}
}
}
/*
** Substitution capture: add result to buffer 'b'
*/
static void substcap (luaL_Buffer *b, CapState *cs) {
const char *curr = cs->cap->s;
if (isfullcap(cs->cap)) /* no nested captures? */
luaL_addlstring(b, curr, cs->cap->siz - 1); /* keep original text */
else {
cs->cap++; /* skip open entry */
while (!isclosecap(cs->cap)) { /* traverse nested captures */
const char *next = cs->cap->s;
luaL_addlstring(b, curr, next - curr); /* add text up to capture */
if (addonestring(b, cs, "replacement"))
curr = closeaddr(cs->cap - 1); /* continue after match */
else /* no capture value */
curr = next; /* keep original text in final result */
}
luaL_addlstring(b, curr, cs->cap->s - curr); /* add last piece of text */
}
cs->cap++; /* go to next capture */
}
/*
** Evaluates a capture and adds its first value to buffer 'b'; returns
** whether there was a value
*/
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {
switch (captype(cs->cap)) {
case Cstring:
stringcap(b, cs); /* add capture directly to buffer */
return 1;
case Csubst:
substcap(b, cs); /* add capture directly to buffer */
return 1;
default: {
lua_State *L = cs->L;
int n = pushcapture(cs);
if (n > 0) {
if (n > 1) lua_pop(L, n - 1); /* only one result */
if (!lua_isstring(L, -1))
luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1));
luaL_addvalue(b);
}
return n;
}
}
}
/*
** Push all values of the current capture into the stack; returns
** number of values pushed
*/
static int pushcapture (CapState *cs) {
lua_State *L = cs->L;
luaL_checkstack(L, 4, "too many captures");
switch (captype(cs->cap)) {
case Cposition: {
lua_pushinteger(L, cs->cap->s - cs->s + 1);
cs->cap++;
return 1;
}
case Cconst: {
pushluaval(cs);
cs->cap++;
return 1;
}
case Carg: {
int arg = (cs->cap++)->idx;
if (arg + FIXEDARGS > cs->ptop)
return luaL_error(L, "reference to absent extra argument #%d", arg);
lua_pushvalue(L, arg + FIXEDARGS);
return 1;
}
case Csimple: {
int k = pushnestedvalues(cs, 1);
lua_insert(L, -k); /* make whole match be first result */
return k;
}
case Cruntime: {
lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */
return 1;
}
case Cstring: {
luaL_Buffer b;
luaL_buffinit(L, &b);
stringcap(&b, cs);
luaL_pushresult(&b);
return 1;
}
case Csubst: {
luaL_Buffer b;
luaL_buffinit(L, &b);
substcap(&b, cs);
luaL_pushresult(&b);
return 1;
}
case Cgroup: {
if (cs->cap->idx == 0) /* anonymous group? */
return pushnestedvalues(cs, 0); /* add all nested values */
else { /* named group: add no values */
nextcap(cs); /* skip capture */
return 0;
}
}
case Cbackref: return backrefcap(cs);
case Ctable: return tablecap(cs);
case Cfunction: return functioncap(cs);
case Cnum: return numcap(cs);
case Cquery: return querycap(cs);
case Cfold: return foldcap(cs);
default: assert(0); return 0;
}
}
/*
** Prepare a CapState structure and traverse the entire list of
** captures in the stack pushing its results. 's' is the subject
** string, 'r' is the final position of the match, and 'ptop'
** the index in the stack where some useful values were pushed.
** Returns the number of results pushed. (If the list produces no
** results, push the final position of the match.)
*/
int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {
Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
int n = 0;
if (!isclosecap(capture)) { /* is there any capture? */
CapState cs;
cs.ocap = cs.cap = capture; cs.L = L;
cs.s = s; cs.valuecached = 0; cs.ptop = ptop;
do { /* collect their values */
n += pushcapture(&cs);
} while (!isclosecap(cs.cap));
}
if (n == 0) { /* no capture values? */
lua_pushinteger(L, r - s + 1); /* return only end position */
n = 1;
}
return n;
}

43
06/deps/lpeglabel/lpcap.h Normal file
View file

@ -0,0 +1,43 @@
/*
** $Id: lpcap.h,v 1.2 2015/02/27 17:13:17 roberto Exp $
*/
#if !defined(lpcap_h)
#define lpcap_h
#include "lptypes.h"
/* kinds of captures */
typedef enum CapKind {
Cclose, Cposition, Cconst, Cbackref, Carg, Csimple, Ctable, Cfunction,
Cquery, Cstring, Cnum, Csubst, Cfold, Cruntime, Cgroup
} CapKind;
typedef struct Capture {
const char *s; /* subject position */
unsigned short idx; /* extra info (group name, arg index, etc.) */
byte kind; /* kind of capture */
byte siz; /* size of full capture + 1 (0 = not a full capture) */
} Capture;
typedef struct CapState {
Capture *cap; /* current capture */
Capture *ocap; /* (original) capture list */
lua_State *L;
int ptop; /* index of last argument to 'match' */
const char *s; /* original string */
int valuecached; /* value stored in cache slot */
} CapState;
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem);
int getcaptures (lua_State *L, const char *s, const char *r, int ptop);
int finddyncap (Capture *cap, Capture *last);
#endif

1035
06/deps/lpeglabel/lpcode.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
/*
** $Id: lpcode.h,v 1.7 2015/06/12 18:24:45 roberto Exp $
*/
#if !defined(lpcode_h)
#define lpcode_h
#include "lua.h"
#include "lptypes.h"
#include "lptree.h"
#include "lpvm.h"
int tocharset (TTree *tree, Charset *cs);
int checkaux (TTree *tree, int pred);
int fixedlenx (TTree *tree, int count, int len);
int hascaptures (TTree *tree);
int lp_gc (lua_State *L);
Instruction *compile (lua_State *L, Pattern *p);
void realloccode (lua_State *L, Pattern *p, int nsize);
int sizei (const Instruction *i);
#define PEnullable 0
#define PEnofail 1
/*
** nofail(t) implies that 't' cannot fail with any input
*/
#define nofail(t) checkaux(t, PEnofail)
/*
** (not nullable(t)) implies 't' cannot match without consuming
** something
*/
#define nullable(t) checkaux(t, PEnullable)
#define fixedlen(t) fixedlenx(t, 0, 0)
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

263
06/deps/lpeglabel/lpprint.c Normal file
View file

@ -0,0 +1,263 @@
/*
** $Id: lpprint.c,v 1.9 2015/06/15 16:09:57 roberto Exp $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#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 void printcapkind (int kind) {
const char *const modes[] = {
"close", "position", "constant", "backref",
"argument", "simple", "table", "function",
"query", "string", "num", "substitution", "fold",
"runtime", "group"};
printf("%s", 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",
"throw", "recovery" /* labeled failure */
};
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: {
printcapkind(getkind(p));
printf(" (size = %d) (idx = %d)", getoff(p), p->i.key);
break;
}
case IOpenCapture: {
printcapkind(getkind(p));
printf(" (idx = %d)", 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;
}
case IThrow: { /* labeled failure */
printf("%d", p->i.aux);
break;
}
case IRecov: { /* labeled failure */
printjmp(op, p);
printcharset((p+2)->buff);
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) {
printcapkind(cap->kind);
printf(" (idx: %d - size: %d) -> %p\n", 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",
"throw", "labeled-choice", "recov" /* labeled failure */
};
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: {
printf(" key: %d\n", tree->key);
break;
}
case TBehind: {
printf(" %d\n", tree->u.n);
printtree(sib1(tree), ident + 2);
break;
}
case TCapture: {
printf(" cap: %d key: %d n: %d\n", tree->cap, tree->key, tree->u.n);
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;
}
case TThrow: { /* labeled failure */
printf(" labels: %d\n", tree->u.label);
break;
}
default: {
int sibs = numsiblings[tree->tag];
printf("\n");
if (tree->tag == TRecov) { /* labeled failure */
printcharset(treelabelset(tree));
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

View file

@ -0,0 +1,36 @@
/*
** $Id: lpprint.h,v 1.2 2015/06/12 18:18:08 roberto Exp $
*/
#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

1363
06/deps/lpeglabel/lptree.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,82 @@
/*
** $Id: lptree.h,v 1.2 2013/03/24 13:51:12 roberto Exp $
*/
#if !defined(lptree_h)
#define lptree_h
#include "lptypes.h"
/*
** types of trees
*/
typedef enum TTag {
TChar = 0, TSet, TAny, /* standard PEG elements */
TTrue, TFalse,
TRep,
TSeq, TChoice,
TNot, TAnd,
TCall,
TOpenCall,
TRule, /* sib1 is rule's pattern, sib2 is 'next' rule */
TGrammar, /* sib1 is initial (and first) rule */
TBehind, /* match behind */
TCapture, /* regular capture */
TRunTime, /* run-time capture */
TThrow, TRecov /* labeled failure */
} TTag;
/* number of siblings for each tree */
extern const byte numsiblings[];
/*
** Tree trees
** The first sibling of a tree (if there is one) is immediately after
** the tree. A reference to a second sibling (ps) is its position
** relative to the position of the tree itself. A key in ktable
** uses the (unique) address of the original tree that created that
** entry. NULL means no data.
*/
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 n; /* occasional counter */
int label; /* labeled failure */
struct { /* labeled failure */
int ps; /* occasional second sibling */
int plab; /* occasional label set */
} s; /* labeled failure */
} 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 siblings for each tree */
extern const byte numsiblings[];
/* access to siblings */
#define sib1(t) ((t) + 1)
#define sib2(t) ((t) + (t)->u.s.ps)
#endif

171
06/deps/lpeglabel/lptypes.h Normal file
View file

@ -0,0 +1,171 @@
/*
** $Id: lptypes.h,v 1.14 2015/09/28 17:17:41 roberto Exp $
** LPeg - PEG pattern matching for Lua
** Copyright 2007-2015, Lua.org & PUC-Rio (see 'lpeg.html' for license)
** written by Roberto Ierusalimschy
*/
#if !defined(lptypes_h)
#define lptypes_h
#if !defined(LPEG_DEBUG)
#define NDEBUG
#endif
#include <assert.h>
#include <limits.h>
#include "lua.h"
#define VERSION "1.0.0"
#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 */
#if !defined(MAXRULES)
#define MAXRULES 1000
#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))))
/* labeled failure begin */
#define MAXLABELS (UCHAR_MAX + 1)
#define LABELSETSIZE CHARSETSIZE
typedef Charset Labelset;
#define setlabel setchar
#define testlabel testchar
/* access to labelset */
#define treelabelset(t) ((byte *)((t) + (t)->u.s.plab))
#define IDXLFAIL 0
#define LFAIL 0
/* update the farthest failure */
#define updatefarthest(s1,s2) { if ((s2) > (s1)) s1 = s2; }
/* labeled failure end */
#endif

429
06/deps/lpeglabel/lpvm.c Normal file
View file

@ -0,0 +1,429 @@
/*
** $Id: lpvm.c,v 1.6 2015/09/28 17:01:25 roberto Exp $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/
#include <limits.h>
#include <string.h>
#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}};
/* labeled failure */
static void setlabelfail(Labelset *ls) {
loopset(i, ls->cs[i] = 0);
ls->cs[IDXLFAIL] = 1;
}
/* labeled failure end */
/*
** {======================================================
** Virtual Machine
** =======================================================
*/
typedef struct Stack {
const char *s; /* saved position (or NULL for calls) */
const Instruction *p; /* next instruction */
const Labelset *ls; /* labeled failure */
int caplevel;
} Stack;
#define getstackbase(L, ptop) ((Stack *)lua_touserdata(L, stackidx(ptop)))
/*
** Double the size of the array of captures
*/
static Capture *doublecap (lua_State *L, Capture *cap, int captop, int ptop) {
Capture *newc;
if (captop >= INT_MAX/((int)sizeof(Capture) * 2))
luaL_error(L, "too many captures");
newc = (Capture *)lua_newuserdata(L, captop * 2 * sizeof(Capture));
memcpy(newc, cap, captop * sizeof(Capture));
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 capture list
** 'base', nested inside a group capture. 'fd' indexes the first capture
** value, 'n' is the number of values (at least 1).
*/
static void adddyncaptures (const char *s, Capture *base, int n, int fd) {
int i;
/* Cgroup capture is already there */
assert(base[0].kind == Cgroup && base[0].siz == 0);
base[0].idx = 0; /* make it an anonymous group */
for (i = 1; i <= n; i++) { /* add runtime captures */
base[i].kind = Cruntime;
base[i].siz = 1; /* mark it as closed */
base[i].idx = fd + i - 1; /* stack index of capture value */
base[i].s = s;
}
base[i].kind = Cclose; /* close group */
base[i].siz = 1;
base[i].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, byte *labelf, const char **sfail) { /* labeled failure */
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 */
const Instruction *pk = NULL; /* resume instruction */
Labelset lsfail;
setlabelfail(&lsfail);
stack->p = &giveup; stack->s = s; stack->ls = &lsfail; stack->caplevel = 0; stack++; /* labeled failure */
*sfail = s; /* labeled failure */
lua_pushlightuserdata(L, stackbase);
for (;;) {
#if defined(DEBUG)
printinst(op, p);
printf("s: |%s| stck:%d, dyncaps:%d, caps:%d ",
s, stack - getstackbase(L, ptop), ndyncap, captop);
printcaplist(capture, capture + captop);
#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 {
*labelf = LFAIL; /* labeled failure */
pk = p + 1;
updatefarthest(*sfail, s); /*labeled failure */
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 {
*labelf = LFAIL; /* labeled failure */
pk = p + 1;
updatefarthest(*sfail, s); /*labeled failure */
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 {
*labelf = LFAIL; /* labeled failure */
pk = p + CHARSETINSTSIZE;
updatefarthest(*sfail, s); /*labeled failure */
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) {
*labelf = LFAIL; /* labeled failure */
pk = p + 1;
updatefarthest(*sfail, s); /*labeled failure */
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->ls = &lsfail; /* labeled failure */
stack->caplevel = captop;
stack++;
p += 2;
continue;
}
case IRecov: { /* labeled failure */
if (stack == stacklimit)
stack = doublestack(L, &stacklimit, ptop);
stack->p = p + getoffset(p);
stack->s = NULL;
stack->ls = (const Labelset *) ((p + 2)->buff);
stack->caplevel = captop;
stack++;
p += (CHARSETINSTSIZE - 1) + 2;
continue;
}
case ICall: {
if (stack == stacklimit)
stack = doublestack(L, &stacklimit, ptop);
stack->s = NULL;
stack->p = p + 2; /* save return address */
stack->ls = NULL;
stack++;
p += getoffset(p);
continue;
}
case ICommit: {
assert(stack > getstackbase(L, ptop) && (stack - 1)->ls != NULL); /* labeled failure */
/*assert((stack - 1)->s != NULL); labeled failure: IRecov does not push s onto the stack */
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 IThrow: { /* labeled failure */
*labelf = p->i.aux;
pk = p + 1;
*sfail = s;
goto fail;
}
case IFailTwice:
assert(stack > getstackbase(L, ptop));
stack--;
/* go through */
case IFail:
*labelf = LFAIL; /* labeled failure */
pk = NULL;
updatefarthest(*sfail, s); /*labeled failure */
fail: { /* pattern failed: try to backtrack */
const Labelset *auxlab = NULL;
Stack *pstack = stack;
do { /* remove pending calls */
assert(pstack > getstackbase(L, ptop));
auxlab = (--pstack)->ls;
} while (auxlab == NULL || !(pstack->p == &giveup || testlabel(pstack->ls->cs, *labelf)));
if (pstack->s != NULL) { /* labeled failure: giveup or backtrack frame */
stack = pstack;
s = stack->s;
if (ndyncap > 0) /* is there matchtime captures? */
ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
captop = stack->caplevel;
} else { /* labeled failure: recovery frame */
if (stack == stacklimit)
stack = doublestack(L, &stacklimit, ptop);
stack->s = NULL;
stack->p = pk; /* save return address */
stack->ls = NULL;
stack->caplevel = captop; /* TODO: really necessary?? */
stack++;
}
p = pstack->p;
continue;
}
case ICloseRunTime: {
CapState cs;
int rem, res, n;
int fr = lua_gettop(L) + 1; /* stack index of first result */
cs.s = o; cs.L = L; cs.ocap = capture; cs.ptop = ptop;
n = runtimecap(&cs, capture + captop, s, &rem); /* call function */
captop -= n; /* remove nested captures */
fr -= rem; /* 'rem' items were popped from Lua stack */
res = resdyncaptures(L, fr, s - o, e - o); /* get result */
if (res == -1) { /* fail? */
*labelf = LFAIL; /* labeled failure */
pk = NULL;
updatefarthest(*sfail, s); /*labeled failure */
goto fail;
}
s = o + res; /* else update current position */
n = lua_gettop(L) - fr + 1; /* number of new captures */
ndyncap += n - rem; /* update number of dynamic captures */
if (n > 0) { /* any new capture? */
if ((captop += n + 2) >= capsize) {
capture = doublecap(L, capture, captop, ptop);
capsize = 2 * captop;
}
/* add new captures to 'capture' list */
adddyncaptures(s, capture + captop - n - 2, n, fr);
}
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);
if (++captop >= capsize) {
capture = doublecap(L, capture, captop, ptop);
capsize = 2 * captop;
}
p++;
continue;
}
default: assert(0); return NULL;
}
}
}
/* }====================================================== */

60
06/deps/lpeglabel/lpvm.h Normal file
View file

@ -0,0 +1,60 @@
/*
** $Id: lpvm.h,v 1.3 2014/02/21 13:06:41 roberto Exp $
*/
#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,
IThrow, /* "fails" with a specific label labeled failure */
IRecov /* stack a recovery; next fail with label 'f' will jump to 'offset' */
} 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, byte *labelf, const char **sfail); /* labeled failure */
#endif

View file

@ -0,0 +1,57 @@
LIBNAME = lpeglabel
LUADIR = ../lua-bootstrap/include/
COPT = -O2
# COPT = -DLPEG_DEBUG -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) -nostdinc -B ../../../05/tcc-bootstrap -I ../../../05/musl-bootstrap/include -I$(LUADIR)
CC = ../../../05/tcc-0.9.27/tcc
FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o
# For Linux
linux:
make lpeglabel.a
# # For Mac OS
# macosx:
# make lpeglabel.so "DLLFLAGS = -bundle -undefined dynamic_lookup"
lpeglabel.a: $(FILES)
$(CC) -ar c lpeglabel.a $(FILES)
$(FILES): makefile
test: test.lua testlabel.lua testerrors.lua relabel.lua lpeglabel.so
lua test.lua
lua testlabel.lua
lua testerrors.lua
clean:
rm -f $(FILES) lpeglabel.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

View file

@ -0,0 +1,399 @@
-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
-- imported functions and modules
local tonumber, type, print, error, ipairs = tonumber, type, print, error, ipairs
local pcall = pcall
local setmetatable = setmetatable
local unpack, tinsert, concat = table.unpack, table.insert, table.concat
local rep = string.rep
-- m is defined in main.c and is equivalent to require ('lpeglabel'
local m = m
-- '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)
local dummy = mm.P(false)
local errinfo = {
{"NoPatt", "no pattern found"},
{"ExtraChars", "unexpected characters after the pattern"},
{"ExpPatt1", "expected a pattern after '/' or '//{...}'"},
{"ExpPatt2", "expected a pattern after '&'"},
{"ExpPatt3", "expected a pattern after '!'"},
{"ExpPatt4", "expected a pattern after '('"},
{"ExpPatt5", "expected a pattern after ':'"},
{"ExpPatt6", "expected a pattern after '{~'"},
{"ExpPatt7", "expected a pattern after '{|'"},
{"ExpPatt8", "expected a pattern after '<-'"},
{"ExpPattOrClose", "expected a pattern or closing '}' after '{'"},
{"ExpNum", "expected a number after '^', '+' or '-' (no space)"},
{"ExpCap", "expected a string, number, '{}' or name after '->'"},
{"ExpName1", "expected the name of a rule after '=>'"},
{"ExpName2", "expected the name of a rule after '=' (no space)"},
{"ExpName3", "expected the name of a rule after '<' (no space)"},
{"ExpLab1", "expected at least one label after '{'"},
{"ExpLab2", "expected a label after the comma"},
{"ExpNameOrLab", "expected a name or label after '%' (no space)"},
{"ExpItem", "expected at least one item after '[' or '^'"},
{"MisClose1", "missing closing ')'"},
{"MisClose2", "missing closing ':}'"},
{"MisClose3", "missing closing '~}'"},
{"MisClose4", "missing closing '|}'"},
{"MisClose5", "missing closing '}'"}, -- for the captures
{"MisClose6", "missing closing '>'"},
{"MisClose7", "missing closing '}'"}, -- for the labels
{"MisClose8", "missing closing ']'"},
{"MisTerm1", "missing terminating single quote"},
{"MisTerm2", "missing terminating double quote"},
}
local errmsgs = {}
local labels = {}
for i, err in ipairs(errinfo) do
errmsgs[i] = err[2]
labels[err[1]] = i
end
local function expect (pattern, labelname)
local label = labels[labelname]
return pattern + m.T(label)
end
-- Pre-defined names
local Predef = { nl = m.P"\n" }
local tlabels = {}
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 getdef (id, defs)
local c = defs and defs[id]
if not c then
error("undefined name: " .. id)
end
return c
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.C(m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0)
local arrow = S * "<-"
-- a defined name only have meaning in a given environment
local Def = name * m.Carg(1)
local num = m.C(m.R"09"^1) * S / tonumber
local String = "'" * m.C((any - "'" - m.P"\n")^0) * expect("'", "MisTerm1")
+ '"' * m.C((any - '"' - m.P"\n")^0) * expect('"', "MisTerm2")
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"\n")
local Class =
"["
* (m.C(m.P"^"^-1)) -- optional complement symbol
* m.Cf(expect(item, "ExpItem") * (item - "]")^0, mt.__add)
/ function (c, p) return c == "^" and any - p or p end
* expect("]", "MisClose8")
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 function choicerec (...)
local t = { ... }
local n = #t
local p = t[1]
local i = 2
while i + 1 <= n do
-- t[i] == nil when there are no labels
p = t[i] and mm.Rec(p, t[i+1], unpack(t[i])) or mt.__add(p, t[i+1])
i = i + 2
end
return p
end
local exp = m.P{ "Exp",
Exp = S * ( m.V"Grammar"
+ (m.V"Seq" * (S * (("//" * m.Ct(m.V"Labels")) + ("/" * m.Cc(nil)))
* expect(S * m.V"Seq", "ExpPatt1")
)^0
) / choicerec);
Labels = m.P"{" * expect(S * m.V"Label", "ExpLab1")
* (S * "," * expect(S * m.V"Label", "ExpLab2"))^0
* expect(S * "}", "MisClose7");
Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix" * (S * m.V"Prefix")^0, mt.__mul);
Prefix = "&" * expect(S * m.V"Prefix", "ExpPatt2") / mt.__len
+ "!" * expect(S * m.V"Prefix", "ExpPatt3") / 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)
+ "^" * expect( m.Cg(num * m.Cc(mult))
+ m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow)
),
"ExpNum")
+ "->" * expect(S * ( m.Cg((String + num) * m.Cc(mt.__div))
+ m.P"{}" * m.Cc(nil, m.Ct)
+ m.Cg(Def / getdef * m.Cc(mt.__div))
),
"ExpCap")
+ "=>" * expect(S * m.Cg(Def / getdef * m.Cc(m.Cmt)),
"ExpName1")
)
)^0, function (a,b,f) return f(a,b) end );
Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1")
+ String / mm.P
+ Class
+ defined
+ "%" * expect(m.P"{", "ExpNameOrLab")
* expect(S * m.V"Label", "ExpLab1")
* expect(S * "}", "MisClose7") / mm.T
+ "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5")
* expect(S * ":}", "MisClose2")
/ function (n, p) return mm.Cg(p, n) end
+ "=" * expect(name, "ExpName2")
/ function (n) return mm.Cmt(mm.Cb(n), equalcap) end
+ m.P"{}" / mm.Cp
+ "{~" * expect(m.V"Exp", "ExpPatt6")
* expect(S * "~}", "MisClose3") / mm.Cs
+ "{|" * expect(m.V"Exp", "ExpPatt7")
* expect(S * "|}", "MisClose4") / mm.Ct
+ "{" * expect(m.V"Exp", "ExpPattOrClose")
* expect(S * "}", "MisClose5") / mm.C
+ m.P"." * m.Cc(any)
+ (name * -arrow + "<" * expect(name, "ExpName3")
* expect(">", "MisClose6")) * m.Cb("G") / NT;
Label = num + name / function (f) return tlabels[f] end;
Definition = name * arrow * expect(m.V"Exp", "ExpPatt8");
Grammar = m.Cg(m.Cc(true), "G")
* m.Cf(m.V"Definition" / firstdef * (S * m.Cg(m.V"Definition"))^0,
adddef) / mm.P;
}
local pattern = S * m.Cg(m.Cc(false), "G") * expect(exp, "NoPatt") / mm.P
* S * expect(-any, "ExtraChars")
local function lineno (s, i)
if i == 1 then return 1, 1 end
local adjustment = 0
-- report the current line if at end of line, not the next
if s:sub(i,i) == '\n' then
i = i-1
adjustment = 1
end
local rest, num = s:sub(1,i):gsub("[^\n]*\n", "")
local r = #rest
return 1 + num, (r ~= 0 and r or 1) + adjustment
end
local function calcline (s, i)
if i == 1 then return 1, 1 end
local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
local col = #rest
return 1 + line, col ~= 0 and col or 1
end
local function splitlines(str)
local t = {}
local function helper(line) tinsert(t, line) return "" end
helper((str:gsub("(.-)\r?\n", helper)))
return t
end
local function compile (p, defs)
if mm.type(p) == "pattern" then return p end -- already compiled
p = p .. " " -- for better reporting of column numbers in errors when at EOF
local ok, cp, label, suffix = pcall(function() return pattern:match(p, 1, defs) end)
if not ok and cp then
if type(cp) == "string" then
cp = cp:gsub("^[^:]+:[^:]+: ", "")
end
error(cp, 3)
end
if not cp then
local lines = splitlines(p)
local line, col = lineno(p, #p - #suffix + 1)
--local line, col = calcline(p, #p - #suffix + 1)
local err = {}
tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errmsgs[label])
tinsert(err, lines[line])
tinsert(err, rep(" ", col-1) .. "^")
error("syntax error(s) in pattern\n" .. concat(err, "\n"), 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
local function setlabels (t)
tlabels = t
end
-- exported names
local re = {
compile = compile,
match = match,
find = find,
gsub = gsub,
updatelocale = updatelocale,
setlabels = setlabels,
calcline = calcline
}
if version == "Lua 5.1" then _G.re = re end
return re

View file

@ -0,0 +1,32 @@
package = "LPegLabel"
version = "0.12.2-1"
source = {
url = "https://github.com/sqmedeiros/lpeglabel/archive/v0.12.2.tar.gz",
tag = "v0.12.2",
dir = "lpeglabel-0.12.2",
}
description = {
summary = "Parsing Expression Grammars For Lua with Labeled Failures",
detailed = [[
LPegLabel is a conservative extension of the LPeg library that provides
an implementation of Parsing Expression Grammars (PEGs) with labeled failures.
Labels can be used to signal different kinds of erros and to specify which
alternative in a labeled ordered choice should handle a given label.
Labels can also be combined with the standard patterns of LPeg.
]],
homepage = "https://github.com/sqmedeiros/lpeglabel/",
maintainer = "Sergio Medeiros <sqmedeiros@gmail.com>",
license = "MIT/X11"
}
dependencies = {
"lua >= 5.2",
}
build = {
type = "builtin",
modules = {
lpeglabel = {
"lpcap.c", "lpcode.c", "lpprint.c", "lptree.c", "lpvm.c"
},
relabel = "relabel.lua"
}
}

View file

@ -0,0 +1,32 @@
package = "LPegLabel"
version = "0.12.2-2"
source = {
url = "https://github.com/sqmedeiros/lpeglabel/archive/v0.12.2-2.tar.gz",
tag = "v0.12.2-2",
dir = "lpeglabel-0.12.2-2",
}
description = {
summary = "Parsing Expression Grammars For Lua with Labeled Failures",
detailed = [[
LPegLabel is a conservative extension of the LPeg library that provides
an implementation of Parsing Expression Grammars (PEGs) with labeled failures.
Labels can be used to signal different kinds of erros and to specify which
alternative in a labeled ordered choice should handle a given label.
Labels can also be combined with the standard patterns of LPeg.
]],
homepage = "https://github.com/sqmedeiros/lpeglabel/",
maintainer = "Sergio Medeiros <sqmedeiros@gmail.com>",
license = "MIT/X11"
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "builtin",
modules = {
lpeglabel = {
"lpcap.c", "lpcode.c", "lpprint.c", "lptree.c", "lpvm.c"
},
relabel = "relabel.lua"
}
}

View file

@ -0,0 +1,32 @@
package = "LPegLabel"
version = "1.0.0-1"
source = {
url = "https://github.com/sqmedeiros/lpeglabel/archive/v1.0.0-1.tar.gz",
tag = "v1.0.0-1",
dir = "lpeglabel-1.0.0-1",
}
description = {
summary = "Parsing Expression Grammars For Lua with Labeled Failures",
detailed = [[
LPegLabel is a conservative extension of the LPeg library that provides
an implementation of Parsing Expression Grammars (PEGs) with labeled failures.
Labels can be used to signal different kinds of erros and to specify which
alternative in a labeled ordered choice should handle a given label.
Labels can also be combined with the standard patterns of LPeg.
]],
homepage = "https://github.com/sqmedeiros/lpeglabel/",
maintainer = "Sergio Medeiros <sqmedeiros@gmail.com>",
license = "MIT/X11"
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "builtin",
modules = {
lpeglabel = {
"lpcap.c", "lpcode.c", "lpprint.c", "lptree.c", "lpvm.c"
},
relabel = "relabel.lua"
}
}

View file

@ -0,0 +1,32 @@
package = "LPegLabel"
version = "1.1.0-1"
source = {
url = "https://github.com/sqmedeiros/lpeglabel/archive/v1.1.0-1.tar.gz",
tag = "v1.1.0-1",
dir = "lpeglabel-1.1.0-1",
}
description = {
summary = "Parsing Expression Grammars For Lua with Labeled Failures",
detailed = [[
LPegLabel is a conservative extension of the LPeg library that provides
an implementation of Parsing Expression Grammars (PEGs) with labeled failures.
Labels can be used to signal different kinds of erros and to specify which
which recovery pattern should handle a given label.
Labels can also be combined with the standard patterns of LPeg.
]],
homepage = "https://github.com/sqmedeiros/lpeglabel/",
maintainer = "Sergio Medeiros <sqmedeiros@gmail.com>",
license = "MIT/X11"
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "builtin",
modules = {
lpeglabel = {
"lpcap.c", "lpcode.c", "lpprint.c", "lptree.c", "lpvm.c"
},
relabel = "relabel.lua"
}
}

View file

@ -0,0 +1,32 @@
package = "LPegLabel"
version = "1.2.0-1"
source = {
url = "https://github.com/sqmedeiros/lpeglabel/archive/v1.2.0-1.tar.gz",
tag = "v1.2.0-1",
dir = "lpeglabel-1.2.0-1",
}
description = {
summary = "Parsing Expression Grammars For Lua with Labeled Failures",
detailed = [[
LPegLabel is a conservative extension of the LPeg library that provides
an implementation of Parsing Expression Grammars (PEGs) with labeled failures.
Labels can be used to signal different kinds of erros and to specify which recovery
pattern should handle a given label.
LPegLabel also reports the farthest failure position in case of an ordinary failure.
]],
homepage = "https://github.com/sqmedeiros/lpeglabel/",
maintainer = "Sergio Medeiros <sqmedeiros@gmail.com>",
license = "MIT/X11"
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "builtin",
modules = {
lpeglabel = {
"lpcap.c", "lpcode.c", "lpprint.c", "lptree.c", "lpvm.c"
},
relabel = "relabel.lua"
}
}

1449
06/deps/lpeglabel/test.lua Executable file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,549 @@
local re = require 'relabel'
function testerror(repatt, msg)
msg = msg:match("^%s*(.-)%s*$") -- trim
local ok, err = pcall(function () re.compile(repatt) end)
assert(not ok)
if msg:match("^[^\n]*\n(.-)$") then
-- expecting a syntax error
err = err:match("^[^\n]*\n(.-)$") -- remove first line (filename)
err = err:gsub("[ \t]*\n", "\n") -- remove trailing spaces
-- if err ~= msg then
-- print(#err, #msg)
-- print('--')
-- print(err)
-- print('--')
-- print(msg)
-- print('--')
-- end
assert(err == msg)
else
-- expecting a non-syntax error
assert(err:match(msg))
end
end
-- testing NoPatt
testerror([[~]], [[
L1:C1: no pattern found
~
^
]])
testerror([[???]], [[
L1:C1: no pattern found
???
^
]])
-- testing ExtraChars
testerror([['p'~]], [[
L1:C4: unexpected characters after the pattern
'p'~
^
]])
testerror([['p'?$?]], [[
L1:C5: unexpected characters after the pattern
'p'?$?
^
]])
-- testing ExpPatt1
testerror([['p' //{1}]], [[
L1:C10: expected a pattern after '/' or '//{...}'
'p' //{1}
^
]])
testerror([['p' //{1} //{2} 'q']], [[
L1:C10: expected a pattern after '/' or '//{...}'
'p' //{1} //{2} 'q'
^
]])
testerror([['p' /]], [[
L1:C6: expected a pattern after '/' or '//{...}'
'p' /
^
]])
testerror([['p' / / 'q']], [[
L1:C6: expected a pattern after '/' or '//{...}'
'p' / / 'q'
^
]])
-- testing ExpPatt2
testerror([[&]], [[
L1:C2: expected a pattern after '&'
&
^
]])
testerror([[& / 'p']], [[
L1:C2: expected a pattern after '&'
& / 'p'
^
]])
testerror([['p' &]], [[
L1:C6: expected a pattern after '&'
'p' &
^
]])
testerror([['p' / & / 'q']], [[
L1:C8: expected a pattern after '&'
'p' / & / 'q'
^
]])
testerror([[&&]], [[
L1:C3: expected a pattern after '&'
&&
^
]])
testerror([[!&]], [[
L1:C3: expected a pattern after '&'
!&
^
]])
-- testing ExpPatt3
testerror([[!]], [[
L1:C2: expected a pattern after '!'
!
^
]])
testerror([[! / 'p']], [[
L1:C2: expected a pattern after '!'
! / 'p'
^
]])
testerror([['p' !]], [[
L1:C6: expected a pattern after '!'
'p' !
^
]])
testerror([['p' / ! / 'q']], [[
L1:C8: expected a pattern after '!'
'p' / ! / 'q'
^
]])
testerror([[!!]], [[
L1:C3: expected a pattern after '!'
!!
^
]])
testerror([[&!]], [[
L1:C3: expected a pattern after '!'
&!
^
]])
-- testing ExpPatt4
testerror([[()]], [[
L1:C2: expected a pattern after '('
()
^
]])
testerror([[($$$)]], [[
L1:C2: expected a pattern after '('
($$$)
^
]])
-- testing ExpPatt5
testerror([[{: *** :}]], [[
L1:C3: expected a pattern after ':'
{: *** :}
^
]])
testerror([[{:group: *** :}]], [[
L1:C9: expected a pattern after ':'
{:group: *** :}
^
]])
testerror([[x <- {:x:}]], [[
L1:C10: expected a pattern after ':'
x <- {:x:}
^
]])
-- testing ExpPatt6
testerror([[{~~}]], [[
L1:C3: expected a pattern after '{~'
{~~}
^
]])
testerror([[{ {~ } ~}]], [[
L1:C5: expected a pattern after '{~'
{ {~ } ~}
^
]])
testerror([[{~ ^_^ ~}]], [[
L1:C3: expected a pattern after '{~'
{~ ^_^ ~}
^
]])
-- testing ExpPatt7
testerror([[{||}]], [[
L1:C3: expected a pattern after '{|'
{||}
^
]])
testerror([[{|@|}]], [[
L1:C3: expected a pattern after '{|'
{|@|}
^
]])
-- testing ExpPatt8
testerror([[S <-]], [[
L1:C5: expected a pattern after '<-'
S <-
^
]])
testerror([[S <- 'p' T <-]], [[
L1:C14: expected a pattern after '<-'
S <- 'p' T <-
^
]])
-- testing ExpPattOrClose
testerror([[{0}]], [[
L1:C2: expected a pattern or closing '}' after '{'
{0}
^
]])
testerror([[{ :'p': }]], [[
L1:C2: expected a pattern or closing '}' after '{'
{ :'p': }
^
]])
-- testing ExpNum
testerror([['p' ^ n]], [[
L1:C6: expected a number after '^', '+' or '-' (no space)
'p' ^ n
^
]])
testerror([['p'^+(+1)]], [[
L1:C5: expected a number after '^', '+' or '-' (no space)
'p'^+(+1)
^
]])
testerror([['p'^-/'q']], [[
L1:C5: expected a number after '^', '+' or '-' (no space)
'p'^-/'q'
^
]])
-- testing ExpCap
testerror([['p' -> {]], [[
L1:C7: expected a string, number, '{}' or name after '->'
'p' -> {
^
]])
testerror([['p' -> {'q'}]], [[
L1:C7: expected a string, number, '{}' or name after '->'
'p' -> {'q'}
^
]])
testerror([['p' -> / 'q']], [[
L1:C7: expected a string, number, '{}' or name after '->'
'p' -> / 'q'
^
]])
testerror([['p' -> [0-9] ]], [[
L1:C7: expected a string, number, '{}' or name after '->'
'p' -> [0-9]
^
]])
-- testing ExpName1
testerror([['p' =>]], [[
L1:C7: expected the name of a rule after '=>'
'p' =>
^
]])
testerror([['p' => 'q']], [[
L1:C7: expected the name of a rule after '=>'
'p' => 'q'
^
]])
-- testing ExpName2
testerror([['<' {:tag: [a-z]+ :} '>' '<' = '>']], [[
L1:C31: expected the name of a rule after '=' (no space)
'<' {:tag: [a-z]+ :} '>' '<' = '>'
^
]])
testerror([['<' {:tag: [a-z]+ :} '>' '<' = tag '>']], [[
L1:C31: expected the name of a rule after '=' (no space)
'<' {:tag: [a-z]+ :} '>' '<' = tag '>'
^
]])
-- testing ExpName3
testerror([[<>]], [[
L1:C2: expected the name of a rule after '<' (no space)
<>
^
]])
testerror([[<123>]], [[
L1:C2: expected the name of a rule after '<' (no space)
<123>
^
]])
testerror([[< hello >]], [[
L1:C2: expected the name of a rule after '<' (no space)
< hello >
^
]])
testerror([[<<S>>]], [[
L1:C2: expected the name of a rule after '<' (no space)
<<S>>
^
]])
-- testing ExpLab1
testerror([['p' //{} 'q']], [[
L1:C8: expected at least one label after '{'
'p' //{} 'q'
^
]])
testerror([[%{ 'label' }]], [[
L1:C3: expected at least one label after '{'
%{ 'label' }
^
]])
-- testing ExpLab2
testerror([['p' //{1,2,3,} 'q']], [[
L1:C14: expected a label after the comma
'p' //{1,2,3,} 'q'
^
]])
-- testing ExpNameOrLab
testerror([[% s]], [[
L1:C2: expected a name or label after '%' (no space)
% s
^
]])
testerror([[% {1}]], [[
L1:C2: expected a name or label after '%' (no space)
% {1}
^
]])
-- testing ExpItem
testerror([[
"p" [
abc
] "q"
]], [[
L1:C6: expected at least one item after '[' or '^'
"p" [
^
]])
-- testing MisClose1
testerror([[('p' ('q' / 'r')]], [[
L1:C17: missing closing ')'
('p' ('q' / 'r')
^
]])
-- testing MisClose2
-- two errors are reported due to the ignore strategy
testerror([[{: group: 'p' :}]], [[
L1:C9: missing closing ':}'
{: group: 'p' :}
^
]])
testerror([[S <- {: 'p' T <- 'q']], [[
L1:C12: missing closing ':}'
S <- {: 'p' T <- 'q'
^
]])
-- testing MisClose3
testerror([['p' {~ ('q' 'r') / 's']], [[
L1:C23: missing closing '~}'
'p' {~ ('q' 'r') / 's'
^
]])
-- testing MisClose4
-- two errors are reported due to the ignore strategy
testerror([['p' {| 'q' / 'r' }]], [[
L1:C17: missing closing '|}'
'p' {| 'q' / 'r' }
^
]])
-- testing MisClose5
testerror([[{ 'p' ]], [[
L1:C6: missing closing '}'
{ 'p'
^
]])
-- testing MisClose6
testerror([[<patt]], [[
L1:C6: missing closing '>'
<patt
^
]])
testerror([[<insert your name here>]], [[
L1:C8: missing closing '>'
<insert your name here>
^
]])
-- testing MisClose7
testerror([['{' %{ a, b '}']], [[
L1:C9: missing closing '}'
'{' %{ a, b '}'
^
]])
-- testing MisClose8
testerror([[[]], [[
L1:C2: missing closing ']'
[
^
]])
testerror([[[^]], [[
L1:C3: missing closing ']'
[^
^
]])
testerror([[[] ]], [[
L1:C4: missing closing ']'
[]
^
]])
testerror([[[^] ]], [[
L1:C6: missing closing ']'
[^]
^
]])
testerror([[[_-___-_|]], [[
L1:C10: missing closing ']'
[_-___-_|
^
]])
-- testing MisTerm1
testerror([['That is the question...]], [[
L1:C25: missing terminating single quote
'That is the question...
^
]])
-- testing MisTerm2
testerror([[Q <- "To be or not to be...]], [[
L1:C28: missing terminating double quote
Q <- "To be or not to be...
^
]])
-- testing non-syntax errors
testerror([[
A <- %nosuch %def
A <- 'A again'
A <- 'and again'
]], [[
name 'nosuch' undefined
]])
testerror([[names not in grammar]], [[
rule 'names' used outside a grammar
]])
testerror([[
A <- %nosuch %def
A <- 'A again'
A <- 'and again'
]], [[
name 'nosuch' undefined
]])
-- the non-syntax error should not be reported
-- since there is a syntax error
testerror([[ A <- %nosuch ('error' ]], [[
L1:C23: missing closing ')'
A <- %nosuch ('error'
^
]])
print 'OK'