Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Dr. Michael Petter
CUP Eclipse Plugin
Commits
6dc02c98
Commit
6dc02c98
authored
Nov 24, 2014
by
Johannes Roith
Browse files
Add "Open Declaration" support + various fixes.
parent
70ab400c
Changes
23
Hide whitespace changes
Inline
Side-by-side
CupParser/src/cup/parser.cup
View file @
6dc02c98
...
...
@@ -664,14 +664,18 @@ preced_type ::=
terminal_list ::= terminal_list:lst COMMA terminal_id:trm
{:
((List<Terminal>) lst).add(new Terminal(trm, null)); // jrTODO: positions
((List<Terminal>) lst).add(new Terminal(trm,
new Range(Position.fromLocation(trmxleft),
Position.fromLocation(trmxright)))); // jrTODO: positions
RESULT = lst;
:}
|
terminal_id:trm
{:
List<Terminal> lst = new ArrayList<Terminal>();
lst.add(new Terminal(trm, null)); // jrTODO: positions
lst.add(new Terminal(trm,
new Range(Position.fromLocation(trmxleft),
Position.fromLocation(trmxright)))); // jrTODO: positions
RESULT = lst;
:}
;
...
...
@@ -941,11 +945,14 @@ prod_part ::=
/*
add
a
labeled
production
part
*/
add_rhs_part
(
add_lab
(
symb
,
labid
));
//
jrTODO
:
we
need
to
check
if
the
production
really
is
labeled
!
ast_add_rhs_part
(
new
LabeledProductionRef
(
new
Name
(
symid
,
null
),
//
jrTODO
:
locations
labid
,
null
));
//
jrTODO
:
locations
ProductionRef
ref
=
null
;
Range
range
=
new
Range
(
Position
.
fromLocation
(
symidxleft
),
Position
.
fromLocation
(
symidxright
));
if
(
labid
==
null
)
ref
=
new
LabeledProductionRef
(
new
Name
(
symid
,
range
),
labid
,
range
);
else
ref
=
new
ProductionRef
(
new
Name
(
symid
,
range
),
range
);
ast_add_rhs_part
(
ref
);
}
:}
|
...
...
@@ -1079,7 +1086,7 @@ new_non_term_id ::=
else
{
if
(
multipart_name
.
equals
(
""
))
{
multipart_name
=
"Object"
;
multipart_name
=
"Object"
;
}
/*
build
the
non
terminal
object
*/
non_terminal
this_nt
=
...
...
@@ -1093,7 +1100,9 @@ new_non_term_id ::=
//
jrTODO
:
positions
!
//
jrTODO
:
non_term_id
vs
.
multipart_name
RESULT
=
new
NonTerminal
(
non_term_id
,
null
);
RESULT
=
new
NonTerminal
(
non_term_id
,
new
Range
(
Position
.
fromLocation
(
non_term_idxleft
),
Position
.
fromLocation
(
non_term_idxright
)));
}
:}
;
...
...
CupParser/src/de/in/tum/www2/cup/Range.java
View file @
6dc02c98
...
...
@@ -26,7 +26,6 @@ public class Range {
public
boolean
contains
(
Position
pos
,
int
removedLength
)
{
if
(
pos
==
null
||
begin
==
null
||
end
==
null
)
return
false
;
if
(
pos
.
getLine
()
>=
begin
.
getLine
()
&&
pos
.
getLine
()
<=
end
.
getLine
())
{
if
(
pos
.
getLine
()
==
begin
.
getLine
()
...
...
@@ -35,16 +34,19 @@ public class Range {
// we are certainly past the start.
if
(
pos
.
getLine
()
==
end
.
getLine
()
&&
pos
.
getColumn
()
>
begin
.
getColumn
())
&&
pos
.
getColumn
()
>
end
.
getColumn
())
return
false
;
// we are certainly not past the end.
throw
new
RuntimeException
(
"not implemented!"
);
// TODO: we really should check, that the amount of code
// remove is not too much.
// return true;
if
(
removedLength
==
0
)
{
return
true
;
}
else
{
throw
new
RuntimeException
(
"not implemented!"
);
// TODO: we really should check, that the amount of code
// remove is not too much.
// return true;
}
}
return
false
;
}
...
...
@@ -53,5 +55,15 @@ public class Range {
return
new
Range
(
Position
.
fromLocation
(
begin
),
Position
.
fromLocation
(
end
));
}
public
String
toString
()
{
StringBuilder
builder
=
new
StringBuilder
();
if
(
begin
!=
null
)
builder
.
append
(
begin
.
toString
());
builder
.
append
(
";"
);
if
(
end
!=
null
)
builder
.
append
(
end
.
toString
());
return
builder
.
toString
();
}
}
CupParser/src/de/in/tum/www2/cup/analysis/AbstractVisitor.java
View file @
6dc02c98
...
...
@@ -3,7 +3,7 @@ package de.in.tum.www2.cup.analysis;
import
de.in.tum.www2.cup.ast.*
;
public
abstract
class
AbstractVisitor
<
T
>
{
{
private
boolean
descend
=
true
;
public
boolean
resetDescend
()
{
...
...
@@ -11,7 +11,7 @@ public abstract class AbstractVisitor<T>
descend
=
true
;
return
res
;
}
// sets visitor state to prevent it from descending to next children.
protected
void
noDescend
()
{
descend
=
false
;
...
...
@@ -21,7 +21,6 @@ public abstract class AbstractVisitor<T>
public
abstract
T
preVisit
(
ClassName
node
,
T
data
);
public
abstract
T
preVisit
(
SpecialCodeBlock
node
,
T
data
);
public
abstract
T
preVisit
(
Import
node
,
T
data
);
public
abstract
T
preVisit
(
LabeledProductionRef
node
,
T
data
);
public
abstract
T
preVisit
(
Name
node
,
T
data
);
public
abstract
T
preVisit
(
NonTerminal
node
,
T
data
);
public
abstract
T
preVisit
(
NonTerminalDeclaration
node
,
T
data
);
...
...
@@ -41,7 +40,6 @@ public abstract class AbstractVisitor<T>
public
abstract
void
postVisit
(
ClassName
node
,
T
data
);
public
abstract
void
postVisit
(
SpecialCodeBlock
node
,
T
data
);
public
abstract
void
postVisit
(
Import
node
,
T
data
);
public
abstract
void
postVisit
(
LabeledProductionRef
node
,
T
data
);
public
abstract
void
postVisit
(
Name
node
,
T
data
);
public
abstract
void
postVisit
(
NonTerminal
node
,
T
data
);
public
abstract
void
postVisit
(
NonTerminalDeclaration
node
,
T
data
);
...
...
CupParser/src/de/in/tum/www2/cup/analysis/FindAtPositionVisitor.java
0 → 100644
View file @
6dc02c98
package
de.in.tum.www2.cup.analysis
;
import
de.in.tum.www2.cup.Position
;
import
de.in.tum.www2.cup.Range
;
import
de.in.tum.www2.cup.ast.*
;
public
class
FindAtPositionVisitor
extends
Visitor
<
Object
>
{
private
AbstractNode
result
;
private
Position
pos
;
/**
* @return The innermost node that corresponds to the position.
*/
public
AbstractNode
getResult
()
{
return
result
;
}
public
FindAtPositionVisitor
(
Position
pos
)
{
this
.
pos
=
pos
;
}
// TODO: does not work for all elements, yet ...
private
void
updateIfMatch
(
AbstractNode
node
)
{
Range
range
=
node
.
getRange
();
if
(
range
==
null
)
return
;
if
(
range
.
contains
(
pos
))
result
=
node
;
}
@Override
public
void
postVisit
(
Name
node
,
Object
data
)
{
if
(
node
==
null
)
return
;
updateIfMatch
(
node
);
}
}
CupParser/src/de/in/tum/www2/cup/analysis/Visitor.java
View file @
6dc02c98
...
...
@@ -21,7 +21,6 @@ public abstract class Visitor<T> extends AbstractVisitor<T>
@Override
public
T
preVisit
(
ClassName
node
,
T
data
)
{
return
data
;
}
@Override
public
T
preVisit
(
SpecialCodeBlock
node
,
T
data
)
{
return
data
;
}
@Override
public
T
preVisit
(
Import
node
,
T
data
)
{
return
data
;
}
@Override
public
T
preVisit
(
LabeledProductionRef
node
,
T
data
)
{
return
data
;
}
@Override
public
T
preVisit
(
Name
node
,
T
data
)
{
return
data
;
}
@Override
public
T
preVisit
(
NonTerminal
node
,
T
data
)
{
return
data
;
}
@Override
public
T
preVisit
(
NonTerminalDeclaration
node
,
T
data
)
{
return
data
;
}
...
...
@@ -41,7 +40,6 @@ public abstract class Visitor<T> extends AbstractVisitor<T>
@Override
public
void
postVisit
(
ClassName
node
,
T
data
)
{
}
@Override
public
void
postVisit
(
SpecialCodeBlock
node
,
T
data
)
{
}
@Override
public
void
postVisit
(
Import
node
,
T
data
)
{
}
@Override
public
void
postVisit
(
LabeledProductionRef
node
,
T
data
)
{
}
@Override
public
void
postVisit
(
Name
node
,
T
data
)
{
}
@Override
public
void
postVisit
(
NonTerminal
node
,
T
data
)
{
}
@Override
public
void
postVisit
(
NonTerminalDeclaration
node
,
T
data
)
{
}
...
...
CupParser/src/de/in/tum/www2/cup/ast/AbstractNode.java
View file @
6dc02c98
...
...
@@ -6,8 +6,17 @@ import de.in.tum.www2.cup.analysis.AbstractVisitor;
public
abstract
class
AbstractNode
{
private
AbstractNode
parent
;
private
Range
range
;
public
AbstractNode
getParent
()
{
return
parent
;
}
void
setParent
(
AbstractNode
parent
)
{
this
.
parent
=
parent
;
}
public
Range
getRange
()
{
return
range
;
}
...
...
@@ -26,6 +35,7 @@ public abstract class AbstractNode {
public
AbstractNode
(
Range
range
)
{
this
.
range
=
range
;
this
.
parent
=
null
;
}
public
abstract
<
T
>
void
accept
(
AbstractVisitor
<
T
>
visitor
,
T
data
);
...
...
CupParser/src/de/in/tum/www2/cup/ast/ClassName.java
View file @
6dc02c98
...
...
@@ -10,6 +10,8 @@ public class ClassName extends AbstractNode {
public
ClassName
(
Name
name
,
Range
range
)
{
super
(
range
);
this
.
name
=
name
;
if
(
name
!=
null
)
name
.
setParent
(
this
);
}
@Override
protected
String
getNodeName
()
{
return
"ClassName"
;
}
...
...
CupParser/src/de/in/tum/www2/cup/ast/Import.java
View file @
6dc02c98
...
...
@@ -10,6 +10,8 @@ public class Import extends AbstractNode {
public
Import
(
Name
name
,
Range
range
)
{
super
(
range
);
this
.
name
=
name
;
if
(
name
!=
null
)
name
.
setParent
(
this
);
}
@Override
protected
String
getNodeName
()
{
return
"Import"
;
}
...
...
CupParser/src/de/in/tum/www2/cup/ast/LabeledProductionRef.java
View file @
6dc02c98
...
...
@@ -4,7 +4,6 @@ import de.in.tum.www2.cup.analysis.AbstractVisitor;
import
de.in.tum.www2.cup.Range
;
public
class
LabeledProductionRef
extends
ProductionRef
implements
IProductionRightPart
{
public
String
label
;
// TODO: Name class?
...
...
@@ -18,12 +17,6 @@ public class LabeledProductionRef extends ProductionRef
@Override
protected
String
getNodeName
()
{
return
"LabeledProductionRef"
;
}
@Override
public
<
T
>
void
accept
(
AbstractVisitor
<
T
>
visitor
,
T
data
)
{
visitor
.
preVisit
(
this
,
data
);
visitor
.
postVisit
(
this
,
data
);
}
@Override
protected
void
putDescription
(
StringBuilder
builder
)
{
builder
.
append
(
label
);
...
...
CupParser/src/de/in/tum/www2/cup/ast/NonTerminalDeclaration.java
View file @
6dc02c98
...
...
@@ -16,6 +16,10 @@ public class NonTerminalDeclaration extends SymbolDeclaration {
public
NonTerminalDeclaration
(
List
<
NonTerminal
>
nonTerminals
,
Range
range
)
{
super
(
range
);
this
.
nonTerminals
=
nonTerminals
;
if
(
nonTerminals
!=
null
)
for
(
NonTerminal
nt
:
nonTerminals
)
if
(
nt
!=
null
)
nt
.
setParent
(
this
);
}
@Override
protected
String
getNodeName
()
{
return
"NonTerminalDeclaration"
;
}
...
...
CupParser/src/de/in/tum/www2/cup/ast/ParserResult.java
View file @
6dc02c98
...
...
@@ -5,6 +5,7 @@ import java.util.*;
import
de.in.tum.www2.cup.Declarations
;
import
de.in.tum.www2.cup.analysis.AbstractVisitor
;
import
de.in.tum.www2.cup.analysis.DeclarationsExtractorVisitor
;
import
de.in.tum.www2.cup.analysis.RefResolutionVisitor
;
public
class
ParserResult
extends
AbstractNode
{
...
...
@@ -13,6 +14,7 @@ public class ParserResult extends AbstractNode {
public
ClassName
className
;
// TODO: these must be ordered as they occur, using a list!!
public
CodeBlock
actionCode
;
public
CodeBlock
parserCode
;
public
CodeBlock
initCode
;
...
...
@@ -39,6 +41,12 @@ public class ParserResult extends AbstractNode {
this
.
accept
(
declVisitor
,
null
);
return
declVisitor
.
getResult
();
}
// TODO: cache in a safe way!
public
void
resolveReferences
()
{
RefResolutionVisitor
resRefVisitor
=
new
RefResolutionVisitor
();
this
.
accept
(
resRefVisitor
,
null
);
}
@Override
public
<
T
>
void
accept
(
AbstractVisitor
<
T
>
visitor
,
T
data
)
{
...
...
CupParser/src/de/in/tum/www2/cup/ast/Precedence.java
View file @
6dc02c98
...
...
@@ -32,6 +32,10 @@ public class Precedence extends AbstractNode {
super
(
range
);
this
.
type
=
typeFromAssoc
(
assoc
);
this
.
terminals
=
lst
;
if
(
terminals
!=
null
)
for
(
Terminal
t
:
terminals
)
if
(
t
!=
null
)
t
.
setParent
(
this
);
}
@Override
protected
String
getNodeName
()
{
return
"Precedence"
;
}
...
...
CupParser/src/de/in/tum/www2/cup/ast/Production.java
View file @
6dc02c98
...
...
@@ -17,8 +17,14 @@ public class Production extends AbstractNode {
public
Production
(
Name
lhs
,
List
<
ProductionRight
>
rhs
,
Range
range
)
{
super
(
range
);
this
.
rhs
=
rhs
;
this
.
lhs
=
lhs
;
if
(
lhs
!=
null
)
this
.
lhs
.
setParent
(
this
);
this
.
rhs
=
rhs
;
if
(
rhs
!=
null
)
for
(
ProductionRight
pr
:
rhs
)
if
(
pr
!=
null
)
pr
.
setParent
(
this
);
}
@Override
protected
String
getNodeName
()
{
return
"Production"
;
}
...
...
CupParser/src/de/in/tum/www2/cup/ast/ProductionRef.java
View file @
6dc02c98
...
...
@@ -3,7 +3,9 @@ package de.in.tum.www2.cup.ast;
import
de.in.tum.www2.cup.analysis.AbstractVisitor
;
import
de.in.tum.www2.cup.Range
;
public
class
ProductionRef
extends
AbstractNode
{
public
class
ProductionRef
extends
AbstractNode
implements
IProductionRightPart
{
private
Name
name
;
private
Production
definitionRef
;
// has to be resolved first.
...
...
@@ -32,6 +34,8 @@ public class ProductionRef extends AbstractNode {
public
ProductionRef
(
Name
name
,
Range
range
)
{
super
(
range
);
this
.
name
=
name
;
if
(
name
!=
null
)
name
.
setParent
(
this
);
this
.
definitionRef
=
null
;
this
.
declarationRef
=
null
;
}
...
...
CupParser/src/de/in/tum/www2/cup/ast/ProductionRight.java
View file @
6dc02c98
...
...
@@ -13,6 +13,20 @@ public class ProductionRight extends AbstractNode {
return
lst
;
}
public
ProductionRight
(
List
<
IProductionRightPart
>
lst
,
Range
range
)
{
super
(
range
);
this
.
lst
=
lst
;
if
(
lst
!=
null
)
{
for
(
IProductionRightPart
prp
:
lst
)
if
(
prp
!=
null
)
{
if
(
prp
instanceof
AbstractNode
)
((
AbstractNode
)
prp
).
setParent
(
this
);
}
}
}
@Override
protected
String
getNodeName
()
{
return
"ProductionRight"
;
}
/*
* Concatenates the right-hand side into a human-readable string.
* */
...
...
@@ -33,13 +47,6 @@ public class ProductionRight extends AbstractNode {
return
builder
.
toString
();
}
public
ProductionRight
(
List
<
IProductionRightPart
>
lst
,
Range
range
)
{
super
(
range
);
this
.
lst
=
lst
;
}
@Override
protected
String
getNodeName
()
{
return
"ProductionRight"
;
}
@Override
public
<
T
>
void
accept
(
AbstractVisitor
<
T
>
visitor
,
T
data
)
{
T
childArg
=
visitor
.
preVisit
(
this
,
data
);
...
...
CupParser/src/de/in/tum/www2/cup/ast/StartWith.java
View file @
6dc02c98
...
...
@@ -10,6 +10,8 @@ public class StartWith extends AbstractNode {
public
StartWith
(
NonTerminal
nt
,
Range
range
)
{
super
(
range
);
this
.
nt
=
nt
;
if
(
nt
!=
null
)
nt
.
setParent
(
this
);
}
@Override
protected
String
getNodeName
()
{
return
"StartWith"
;
}
...
...
CupParser/src/de/in/tum/www2/cup/ast/TerminalDeclaration.java
View file @
6dc02c98
...
...
@@ -16,6 +16,10 @@ public class TerminalDeclaration extends SymbolDeclaration {
public
TerminalDeclaration
(
List
<
Terminal
>
terminals
,
Range
range
)
{
super
(
range
);
this
.
terminals
=
terminals
;
if
(
terminals
!=
null
)
for
(
Terminal
t
:
terminals
)
if
(
t
!=
null
)
t
.
setParent
(
this
);
}
@Override
protected
String
getNodeName
()
{
return
"TerminalDeclaration"
;
}
...
...
CupPlugin/src/de/tum/in/www2/cupplugin/commands/OpenDeclarationHandler.java
View file @
6dc02c98
...
...
@@ -12,7 +12,7 @@ public class OpenDeclarationHandler extends AbstractHandler {
public
Object
execute
(
ExecutionEvent
event
)
throws
ExecutionException
{
PluginUtility
.
showMessage
(
"OpenDeclarationHandler command called!"
);
return
null
;
}
...
...
CupPlugin/src/de/tum/in/www2/cupplugin/commands/OpenDefinitionHandler.java
View file @
6dc02c98
...
...
@@ -3,15 +3,67 @@ package de.tum.in.www2.cupplugin.commands;
import
org.eclipse.core.commands.AbstractHandler
;
import
org.eclipse.core.commands.ExecutionEvent
;
import
org.eclipse.core.commands.ExecutionException
;
import
org.eclipse.jface.text.BadLocationException
;
import
org.eclipse.jface.text.IDocument
;
import
org.eclipse.jface.text.ITextSelection
;
import
org.eclipse.ui.handlers.HandlerUtil
;
import
org.eclipse.ui.texteditor.IDocumentProvider
;
import
de.in.tum.www2.cup.Position
;
import
de.in.tum.www2.cup.Range
;
import
de.in.tum.www2.cup.analysis.FindAtPositionVisitor
;
import
de.in.tum.www2.cup.ast.AbstractNode
;
import
de.in.tum.www2.cup.ast.Name
;
import
de.in.tum.www2.cup.ast.NonTerminalDeclaration
;
import
de.in.tum.www2.cup.ast.ParserResult
;
import
de.in.tum.www2.cup.ast.Production
;
import
de.in.tum.www2.cup.ast.ProductionRef
;
import
de.tum.in.www2.cupplugin.controller.PluginUtility
;
import
de.tum.in.www2.cupplugin.editors.CupTextEditor
;
import
de.tum.in.www2.cupplugin.editors.MultiPageEditor
;
import
de.tum.in.www2.cupplugin.model.Model
;
public
class
OpenDefinitionHandler
extends
AbstractHandler
{
@Override
public
Object
execute
(
ExecutionEvent
event
)
throws
ExecutionException
{
MultiPageEditor
mpe
=
(
MultiPageEditor
)
HandlerUtil
.
getActiveEditorChecked
(
event
);
CupTextEditor
editor
=
mpe
.
getEditor
();
Range
range
=
null
;
try
{
range
=
PluginUtility
.
getRangeFromSelection
(
editor
);
}
catch
(
BadLocationException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
if
(
range
==
null
)
return
null
;
IDocument
doc
=
editor
.
getDocumentProvider
().
getDocument
(
editor
.
getEditorInput
());
Model
model
=
Model
.
getInstanceForDocument
(
doc
);
ParserResult
result
=
model
.
getAstModel
();
if
(
result
!=
null
)
{
result
.
resolveReferences
();
FindAtPositionVisitor
pf
=
new
FindAtPositionVisitor
(
range
.
getBegin
());
result
.
accept
(
pf
,
null
);
AbstractNode
node
=
pf
.
getResult
();
if
(
node
!=
null
)
{
AbstractNode
parent
=
node
.
getParent
();
if
(
parent
!=
null
&&
parent
instanceof
ProductionRef
)
{
ProductionRef
prodRef
=
(
ProductionRef
)
parent
;
PluginUtility
.
showMessage
(
"OpenDefinitionHandler command called!"
);
Production
definition
=
prodRef
.
getDefinition
();
if
(
definition
!=
null
)
{
Position
targetPosition
=
definition
.
getBegin
();
int
length
=
definition
.
getName
().
name
.
length
();
editor
.
selectAndReveal
(
targetPosition
.
getOffsetFromStart
(),
length
);
}
}
}
}
return
null
;
}
...
...
CupPlugin/src/de/tum/in/www2/cupplugin/controller/PluginUtility.java
View file @
6dc02c98
...
...
@@ -4,8 +4,17 @@ import org.eclipse.core.resources.IContainer;
import
org.eclipse.core.resources.IFile
;
import
org.eclipse.swt.widgets.MessageBox
;
import
org.eclipse.ui.PlatformUI
;
import
org.eclipse.ui.texteditor.IDocumentProvider
;
import
org.eclipse.core.resources.IResource
;
import
org.eclipse.jface.text.BadLocationException
;
import
org.eclipse.jface.text.IDocument
;
import
org.eclipse.jface.text.ITextSelection
;
import
de.in.tum.www2.cup.Position
;
import
de.in.tum.www2.cup.Range
;
import
de.in.tum.www2.cup.ast.ParserResult
;
import
de.tum.in.www2.cupplugin.editors.CupTextEditor
;
import
de.tum.in.www2.cupplugin.model.Model
;
public
final
class
PluginUtility
{
...
...
@@ -15,6 +24,34 @@ public final class PluginUtility {
mb
.
open
();
}
public
static
Range
getRangeFromSelection
(
CupTextEditor
editor
)
throws
BadLocationException
{
ITextSelection
textSelection
=
(
ITextSelection
)
editor
.
getSite
().
getSelectionProvider
().
getSelection
();
int
offset
=
textSelection
.
getOffset
();
IDocumentProvider
provider
=
editor
.
getDocumentProvider
();
IDocument
document
=
provider
.
getDocument
(
editor
.
getEditorInput
());
// TODO: there might be off-by-one errors here.
// Check again!
int
startLineNumber
=
document
.
getLineOfOffset
(
offset
);
int
startColumn
=
offset
-
document
.
getLineOffset
(
startLineNumber
);
Position
start
=
new
Position
(
startLineNumber
+
1
,
startColumn
+
1
,
offset
+
1
);
int
endOffset
=
offset
+
textSelection
.
getLength
();
int
endLineNumber
=
document
.
getLineOfOffset
(
endOffset
);
int
endColumn
=
endOffset
-
document
.
getLineOffset
(
endLineNumber
);
Position
end
=
new
Position
(
endLineNumber
+
1
,
endColumn
+
1
,
endOffset
);
return
new
Range
(
start
,
end
);
}
/**
* Find the generated file that matches our cup file.
* @param cupFile The original cup file.
...
...
Prev
1
2
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment