Add stage 06: Lua bootstrap
The goal of stage 06 is to try parse zig synax in lua. I pulled in lpeglable 1.2.0 and parser-gen off github to get started. All of this needs to be cleaned up rather soon. Lua boostraps using tcc and musl from the previous stage. Since musl 0.6.0 doesn't support dynamic linking this build of lua doesn't support shared libraries. I couldn't easily patch musl with dlopen and friends so instead I link statically and call deps with c api.
This commit is contained in:
parent
2ae045cf8a
commit
e6b88d5a0f
170 changed files with 72518 additions and 2 deletions
2
06/lpeglabel/.gitignore
vendored
Normal file
2
06/lpeglabel/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
lpeglabel.so
|
||||
*.o
|
32
06/lpeglabel/HISTORY
Normal file
32
06/lpeglabel/HISTORY
Normal 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/lpeglabel/LICENSE
Normal file
22
06/lpeglabel/LICENSE
Normal 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/lpeglabel/README.md
Normal file
727
06/lpeglabel/README.md
Normal 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
|
||||
```
|
128
06/lpeglabel/examples/expRec.lua
Normal file
128
06/lpeglabel/examples/expRec.lua
Normal 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")
|
||||
|
122
06/lpeglabel/examples/expRecAut.lua
Normal file
122
06/lpeglabel/examples/expRecAut.lua
Normal 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]
|
14
06/lpeglabel/examples/farthest.lua
Normal file
14
06/lpeglabel/examples/farthest.lua
Normal 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
|
||||
|
||||
|
33
06/lpeglabel/examples/listId1.lua
Normal file
33
06/lpeglabel/examples/listId1.lua
Normal 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,"))
|
39
06/lpeglabel/examples/listId2.lua
Normal file
39
06/lpeglabel/examples/listId2.lua
Normal 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,"))
|
67
06/lpeglabel/examples/listId2Rec2.lua
Normal file
67
06/lpeglabel/examples/listId2Rec2.lua
Normal 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,"))
|
79
06/lpeglabel/examples/listId2Rec2Cap.lua
Normal file
79
06/lpeglabel/examples/listId2Rec2Cap.lua
Normal 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,")
|
30
06/lpeglabel/examples/listIdRe1.lua
Normal file
30
06/lpeglabel/examples/listIdRe1.lua
Normal 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,"))
|
||||
|
71
06/lpeglabel/examples/listIdRe2.lua
Normal file
71
06/lpeglabel/examples/listIdRe2.lua
Normal 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,")
|
151
06/lpeglabel/examples/tiny.lua
Normal file
151
06/lpeglabel/examples/tiny.lua
Normal 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"))
|
||||
|
2572
06/lpeglabel/examples/typedlua/test.lua
Executable file
2572
06/lpeglabel/examples/typedlua/test.lua
Executable file
File diff suppressed because it is too large
Load diff
66
06/lpeglabel/examples/typedlua/tlerror.lua
Normal file
66
06/lpeglabel/examples/typedlua/tlerror.lua
Normal 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,
|
||||
}
|
105
06/lpeglabel/examples/typedlua/tllexer.lua
Normal file
105
06/lpeglabel/examples/typedlua/tllexer.lua
Normal 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
|
20
06/lpeglabel/examples/typedlua/tlp.lua
Normal file
20
06/lpeglabel/examples/typedlua/tlp.lua
Normal 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)
|
245
06/lpeglabel/examples/typedlua/tlparser.lua
Normal file
245
06/lpeglabel/examples/typedlua/tlparser.lua
Normal 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/lpeglabel/lpcap.c
Normal file
537
06/lpeglabel/lpcap.c
Normal 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/lpeglabel/lpcap.h
Normal file
43
06/lpeglabel/lpcap.h
Normal 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/lpeglabel/lpcode.c
Normal file
1035
06/lpeglabel/lpcode.c
Normal file
File diff suppressed because it is too large
Load diff
42
06/lpeglabel/lpcode.h
Normal file
42
06/lpeglabel/lpcode.h
Normal 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
|
BIN
06/lpeglabel/lpeglabel-logo.png
Normal file
BIN
06/lpeglabel/lpeglabel-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
263
06/lpeglabel/lpprint.c
Normal file
263
06/lpeglabel/lpprint.c
Normal 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
|
36
06/lpeglabel/lpprint.h
Normal file
36
06/lpeglabel/lpprint.h
Normal 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/lpeglabel/lptree.c
Normal file
1363
06/lpeglabel/lptree.c
Normal file
File diff suppressed because it is too large
Load diff
82
06/lpeglabel/lptree.h
Normal file
82
06/lpeglabel/lptree.h
Normal 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/lpeglabel/lptypes.h
Normal file
171
06/lpeglabel/lptypes.h
Normal 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/lpeglabel/lpvm.c
Normal file
429
06/lpeglabel/lpvm.c
Normal 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/lpeglabel/lpvm.h
Normal file
60
06/lpeglabel/lpvm.h
Normal 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
|
||||
|
57
06/lpeglabel/makefile
Normal file
57
06/lpeglabel/makefile
Normal 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.so "DLLFLAGS = -shared -nostdlib -B ../../05/tcc-bootstrap "
|
||||
|
||||
# # For Mac OS
|
||||
# macosx:
|
||||
# make lpeglabel.so "DLLFLAGS = -bundle -undefined dynamic_lookup"
|
||||
|
||||
lpeglabel.so: $(FILES)
|
||||
$(CC) $(DLLFLAGS) $(FILES) -o lpeglabel.so ../lua-bootstrap/lib/liblua.a ../../05/musl-bootstrap/lib/libc.a
|
||||
|
||||
$(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
|
||||
|
60
06/lpeglabel/old_makefile
Normal file
60
06/lpeglabel/old_makefile
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
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 = lplvm.o lplcap.o lpltree.o lplcode.o lplprint.o
|
||||
# For Linux
|
||||
linux:
|
||||
make lpeglabel.so "DLLFLAGS = -shared -nostdlib -B ../../05/tcc-bootstrap "
|
||||
|
||||
# For Mac OS
|
||||
# macosx:
|
||||
# make lpeglabel.so "DLLFLAGS = -bundle -undefined dynamic_lookup"
|
||||
|
||||
# For Windows
|
||||
# windows:
|
||||
# make lpeglabel.dll "DLLFLAGS = -shared -fPIC"
|
||||
|
||||
lpeglabel.so: $(FILES)
|
||||
$(CC) $(DLLFLAGS) $(FILES) -o lpeglabel.so ../lua-bootstrap/lib/liblua.a ../../05/musl-bootstrap/lib/libc.a
|
||||
lpeglabel.dll: $(FILES)
|
||||
$(CC) $(DLLFLAGS) $(FILES) -o lpeglabel.dll $(LUADIR)/bin/lua53.dll
|
||||
|
||||
$(FILES): makefile
|
||||
|
||||
test: test.lua testlabel.lua testrelabelparser.lua relabel.lua lpeglabel.so
|
||||
lua test.lua
|
||||
lua testlabel.lua
|
||||
lua testrelabelparser.lua
|
||||
|
||||
clean:
|
||||
rm -f $(FILES) lpeglabel.so
|
||||
|
||||
|
||||
lplcap.o: lplcap.c lplcap.h lpltypes.h
|
||||
lplcode.o: lplcode.c lpltypes.h lplcode.h lpltree.h lplvm.h lplcap.h
|
||||
lplprint.o: lplprint.c lpltypes.h lplprint.h lpltree.h lplvm.h lplcap.h
|
||||
lpltree.o: lpltree.c lpltypes.h lplcap.h lplcode.h lpltree.h lplvm.h lplprint.h
|
||||
lplvm.o: lplvm.c lplcap.h lpltypes.h lplvm.h lplprint.h lpltree.h
|
||||
|
398
06/lpeglabel/relabel.lua
Normal file
398
06/lpeglabel/relabel.lua
Normal file
|
@ -0,0 +1,398 @@
|
|||
-- $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 or unpack, table.insert, table.concat
|
||||
local rep = string.rep
|
||||
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
|
32
06/lpeglabel/rockspecs/lpeglabel-0.12.2-1.rockspec
Normal file
32
06/lpeglabel/rockspecs/lpeglabel-0.12.2-1.rockspec
Normal 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"
|
||||
}
|
||||
}
|
32
06/lpeglabel/rockspecs/lpeglabel-0.12.2-2.rockspec
Normal file
32
06/lpeglabel/rockspecs/lpeglabel-0.12.2-2.rockspec
Normal 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"
|
||||
}
|
||||
}
|
32
06/lpeglabel/rockspecs/lpeglabel-1.0.0-1.rockspec
Normal file
32
06/lpeglabel/rockspecs/lpeglabel-1.0.0-1.rockspec
Normal 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"
|
||||
}
|
||||
}
|
32
06/lpeglabel/rockspecs/lpeglabel-1.1.0-1.rockspec
Normal file
32
06/lpeglabel/rockspecs/lpeglabel-1.1.0-1.rockspec
Normal 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"
|
||||
}
|
||||
}
|
32
06/lpeglabel/rockspecs/lpeglabel-1.2.0-1.rockspec
Normal file
32
06/lpeglabel/rockspecs/lpeglabel-1.2.0-1.rockspec
Normal 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/lpeglabel/test.lua
Executable file
1449
06/lpeglabel/test.lua
Executable file
File diff suppressed because it is too large
Load diff
1054
06/lpeglabel/testlabel.lua
Normal file
1054
06/lpeglabel/testlabel.lua
Normal file
File diff suppressed because it is too large
Load diff
549
06/lpeglabel/testrelabelparser.lua
Normal file
549
06/lpeglabel/testrelabelparser.lua
Normal 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'
|
Loading…
Add table
Add a link
Reference in a new issue