mirror of
https://github.com/Minres/RDL-Editor.git
synced 2025-07-07 16:23:26 +02:00
Changed grammar and added code generator
* changed the grammar to ease code generation * added a code generator and a standalone setup to generate SystemC code using SC-Components lib
This commit is contained in:
@ -0,0 +1,32 @@
|
||||
package com.minres.rdl.generator
|
||||
|
||||
import com.minres.rdl.generator.RdlBaseGenerator
|
||||
import com.minres.rdl.rdl.ComponentDefinition
|
||||
|
||||
class AddrmapGenerator extends RdlBaseGenerator {
|
||||
|
||||
val ComponentDefinition componentDefinition
|
||||
|
||||
new(ComponentDefinition definition) {
|
||||
componentDefinition=definition
|
||||
}
|
||||
|
||||
override generateHeader() {'''
|
||||
#ifndef _E300_PLAT_MAP_H_
|
||||
#define _E300_PLAT_MAP_H_
|
||||
// need double braces, see https://stackoverflow.com/questions/6893700/how-to-construct-stdarray-object-with-initializer-list#6894191
|
||||
const std::array<sysc::target_memory_map_entry<32>, 3> e300_plat_map = {{
|
||||
{&i_gpio, 0x10012000, 0x1000},
|
||||
{&i_uart, 0x10013000, 0x1000},
|
||||
{&i_spi, 0x10014000, 0x1000}
|
||||
}};
|
||||
|
||||
#endif /* _E300_PLAT_MAP_H_ */
|
||||
'''
|
||||
}
|
||||
|
||||
override generateSource() {
|
||||
''
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
package com.minres.rdl.generator
|
||||
|
||||
|
||||
import com.google.inject.Inject
|
||||
import com.google.inject.Provider
|
||||
import com.minres.rdl.RDLStandaloneSetup
|
||||
import java.lang.reflect.MalformedParametersException
|
||||
import java.util.ArrayList
|
||||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.emf.ecore.resource.ResourceSet
|
||||
import org.eclipse.emf.mwe.utils.ProjectMapping
|
||||
import org.eclipse.emf.mwe.utils.StandaloneSetup
|
||||
import org.eclipse.xtext.generator.GeneratorContext
|
||||
import org.eclipse.xtext.generator.GeneratorDelegate
|
||||
import org.eclipse.xtext.generator.JavaIoFileSystemAccess
|
||||
import org.eclipse.xtext.resource.XtextResource
|
||||
import org.eclipse.xtext.resource.XtextResourceSet
|
||||
import org.eclipse.xtext.util.CancelIndicator
|
||||
import org.eclipse.xtext.validation.CheckMode
|
||||
import org.eclipse.xtext.validation.IResourceValidator
|
||||
import java.text.ParseException
|
||||
|
||||
class Main {
|
||||
static class Option {
|
||||
String flag
|
||||
String value
|
||||
def Option(String flag, String value) {
|
||||
this.flag = flag
|
||||
this.value = value
|
||||
}
|
||||
}
|
||||
|
||||
def static main(String[] args) {
|
||||
if (args.empty) {
|
||||
System::err.println('Aborting: no path to EMF resource provided!')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val injector = new RDLStandaloneSetup().createInjectorAndDoEMFRegistration
|
||||
injector.getInstance(Main).run(args)
|
||||
} catch(MalformedParametersException e){
|
||||
print("Command line error "+e.message)
|
||||
} catch(IllegalArgumentException e){
|
||||
print("generation error "+e.message)
|
||||
e.printStackTrace
|
||||
} catch(ParseException e){
|
||||
print("parse problem "+e.message+" ("+ e.errorOffset+")")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Inject Provider<ResourceSet> resourceSetProvider
|
||||
|
||||
@Inject IResourceValidator validator
|
||||
|
||||
@Inject GeneratorDelegate generator
|
||||
|
||||
@Inject JavaIoFileSystemAccess fileAccess
|
||||
|
||||
var optsList = new ArrayList<Option>();
|
||||
var argsList = new ArrayList<String>()
|
||||
val shortOptMap = newLinkedHashMap('i' -> 'incl-out', 's' -> 'src-out')
|
||||
|
||||
def protected parseOptions(String[] args) {
|
||||
for (arg : args) {
|
||||
switch (arg) {
|
||||
case arg.startsWith('--'): {
|
||||
if (arg.length < 3)
|
||||
throw new MalformedParametersException("not a valid argument: " + arg);
|
||||
val res = arg.substring(2).split('=')
|
||||
var opt = new Option()
|
||||
val longOpt = shortOptMap.values.findFirst[String s|s == res.get(0)]
|
||||
if(longOpt === null) throw new IllegalArgumentException("unknown option: " + arg);
|
||||
opt.flag = res.get(0)
|
||||
if (res.size == 2)
|
||||
opt.value = res.get(1)
|
||||
optsList += opt
|
||||
}
|
||||
case arg.startsWith('-'): {
|
||||
if (arg.length < 2)
|
||||
throw new MalformedParametersException("not a valid argument: " + arg);
|
||||
// -opt
|
||||
var res = arg.substring(1).split('=')
|
||||
val longOpt = shortOptMap.get(res.get(0))
|
||||
if(longOpt === null) throw new MalformedParametersException("unknown option: " + arg);
|
||||
var opt = new Option()
|
||||
opt.flag = longOpt
|
||||
if (res.size == 2)
|
||||
opt.value = res.get(1)
|
||||
optsList += opt
|
||||
|
||||
}
|
||||
default: {
|
||||
argsList += arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def run(String[] args){
|
||||
parseOptions(args)
|
||||
val repo = optsList.findFirst[it.flag == "repository"]
|
||||
if(repo!==null){
|
||||
val projectMapping = new ProjectMapping
|
||||
projectMapping.projectName = "RDL Repository"
|
||||
projectMapping.path = repo.value
|
||||
new StandaloneSetup().addProjectMapping(projectMapping)
|
||||
}
|
||||
argsList.forEach[runGenerator(it)]
|
||||
}
|
||||
|
||||
def protected runGenerator(String string) {
|
||||
// Load the resource
|
||||
val resourceSet = resourceSetProvider.get as XtextResourceSet
|
||||
resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
|
||||
val resource = resourceSet.getResource(URI.createFileURI(string), true)
|
||||
// Validate the resource
|
||||
val issues = validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl)
|
||||
if (!issues.empty) {
|
||||
System.err.println("Error validating "+resource.URI)
|
||||
issues.forEach[System.err.println(it)]
|
||||
throw new ParseException("error validating "+resource.URI, issues.size)
|
||||
}
|
||||
// Configure and start the generator
|
||||
fileAccess.outputPath = 'src-gen/'
|
||||
optsList.filter[it.flag.matches('.*-out')].forEach[fileAccess.setOutputPath(it.flag, it.value)]
|
||||
fileAccess.outputConfigurations.get('src-out')?.setOverrideExistingResources(true)
|
||||
|
||||
val context = new GeneratorContext => [ cancelIndicator = CancelIndicator.NullImpl ]
|
||||
generator.generate(resource, fileAccess, context)
|
||||
|
||||
System.out.print('Code generation for '+string +' finished, ')
|
||||
try{
|
||||
System.out.print('includes are in '+fileAccess.getURI('', 'incl-out')+', ')
|
||||
}catch(Exception e){
|
||||
System.out.print('includes are in '+fileAccess.getURI('')+', ')
|
||||
}
|
||||
try{
|
||||
System.out.println('sources are in '+fileAccess.getURI('', 'src-out')+', ')
|
||||
}catch(Exception e){
|
||||
System.out.println('sources are in '+fileAccess.getURI('')+', ')
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@
|
||||
*/
|
||||
package com.minres.rdl.generator
|
||||
|
||||
import com.minres.rdl.rdl.ComponentDefinition
|
||||
import com.minres.rdl.rdl.ComponentDefinitionType
|
||||
import org.eclipse.emf.ecore.resource.Resource
|
||||
import org.eclipse.xtext.generator.AbstractGenerator
|
||||
import org.eclipse.xtext.generator.IFileSystemAccess2
|
||||
@ -16,10 +18,29 @@ import org.eclipse.xtext.generator.IGeneratorContext
|
||||
class RDLGenerator extends AbstractGenerator {
|
||||
|
||||
override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
|
||||
// fsa.generateFile('greetings.txt', 'People to greet: ' +
|
||||
// resource.allContents
|
||||
// .filter(Greeting)
|
||||
// .map[name]
|
||||
// .join(', '))
|
||||
}
|
||||
}
|
||||
resource.resourceSet.allContents.filter[ it instanceof ComponentDefinition].map[it as ComponentDefinition].forEach[
|
||||
val gen = it.fileGenerator
|
||||
if(gen!==null){
|
||||
val header = gen.generateHeader
|
||||
if(header!==null && header.length>0) fsa.generateFile(it.name+'.h', fsa.outputConfig('incl-out'), header)
|
||||
val source = gen.generateSource
|
||||
if(source!==null && source.length>0) fsa.generateFile(it.name+'.cpp', fsa.outputConfig('src-out'), source)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def RdlBaseGenerator fileGenerator(ComponentDefinition definition){
|
||||
switch(definition.type){
|
||||
case ComponentDefinitionType.REGFILE: new RegfileGenerator(definition)
|
||||
case ComponentDefinitionType.ADDRMAP: new AddrmapGenerator(definition)
|
||||
default: null
|
||||
}
|
||||
}
|
||||
|
||||
def String outputConfig(IFileSystemAccess2 fsa, String string){
|
||||
var output_config = string
|
||||
try {fsa.getURI("", output_config)} catch (Exception e) {output_config ='DEFAULT_OUTPUT'}
|
||||
return output_config
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
package com.minres.rdl.generator
|
||||
|
||||
import com.minres.rdl.rdl.ComponentDefinition
|
||||
import com.minres.rdl.rdl.ExplicitPropertyAssignment
|
||||
import com.minres.rdl.rdl.PropertyAssignment
|
||||
import com.minres.rdl.rdl.PropertyEnum
|
||||
import com.minres.rdl.IntegerWithRadix
|
||||
import com.minres.rdl.rdl.Instantiation
|
||||
import com.minres.rdl.rdl.PropertyAssignmentRhs
|
||||
import com.minres.rdl.rdl.RValue
|
||||
import com.minres.rdl.rdl.RValueConstant
|
||||
import com.minres.rdl.rdl.InstancePropertyRef
|
||||
import com.minres.rdl.rdl.ComponentDefinitionType
|
||||
|
||||
abstract class RdlBaseGenerator {
|
||||
def long accessWidth(ComponentDefinition definition){
|
||||
var size = 32L
|
||||
val pa = definition.propertyAssignments.findFirst[PropertyAssignment pa |
|
||||
pa instanceof ExplicitPropertyAssignment && (pa as ExplicitPropertyAssignment).name==PropertyEnum.ACCESSWIDTH
|
||||
]
|
||||
if(pa !== null){
|
||||
val sz = new IntegerWithRadix((pa as ExplicitPropertyAssignment).rhs.effectiveValue)
|
||||
size=sz.value
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
def long getSize(Instantiation instantiation){
|
||||
val componentDef= instantiation.definingComponent
|
||||
switch (componentDef.type) {
|
||||
case ComponentDefinitionType.REG: {
|
||||
val pa = componentDef.propertyAssignments.findFirst[PropertyAssignment pa |
|
||||
pa instanceof ExplicitPropertyAssignment && (pa as ExplicitPropertyAssignment).name==PropertyEnum.REGWIDTH
|
||||
]
|
||||
if(pa !== null){
|
||||
val sz = new IntegerWithRadix((pa as ExplicitPropertyAssignment).rhs.effectiveValue)
|
||||
return sz.value
|
||||
}
|
||||
return 32L
|
||||
}
|
||||
case ComponentDefinitionType.FIELD:{
|
||||
val pa = componentDef.propertyAssignments.findFirst[PropertyAssignment pa |
|
||||
pa instanceof ExplicitPropertyAssignment && (pa as ExplicitPropertyAssignment).name==PropertyEnum.FIELDWIDTH
|
||||
]
|
||||
if(pa !== null){
|
||||
val sz = new IntegerWithRadix((pa as ExplicitPropertyAssignment).rhs.effectiveValue)
|
||||
return sz.value
|
||||
}
|
||||
return 1L
|
||||
}
|
||||
default: {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def String effectiveName(ComponentDefinition definition){
|
||||
if(definition.name!==null){
|
||||
definition.name
|
||||
} else{
|
||||
val pa = definition.propertyAssignments.findFirst[PropertyAssignment pa |
|
||||
pa instanceof ExplicitPropertyAssignment && (pa as ExplicitPropertyAssignment).name==PropertyEnum.NAME
|
||||
]
|
||||
(pa as ExplicitPropertyAssignment).rhs.effectiveValue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def String effectiveValue(PropertyAssignmentRhs rhs){
|
||||
if(rhs.value!== null)
|
||||
rhs.value.effectiveValue
|
||||
else if(rhs.instPropRef!==null)
|
||||
rhs.instPropRef.effectiveValue
|
||||
else if(rhs.enumRef!==null)
|
||||
rhs.enumRef.name
|
||||
}
|
||||
|
||||
def String effectiveValue(RValue rvalue){
|
||||
if(rvalue.str!==null){
|
||||
rvalue.str
|
||||
} else if(rvalue.^val!=RValueConstant.UNDEFINED)
|
||||
rvalue.^val.literal
|
||||
else if(rvalue.num!==null){
|
||||
val num = rvalue.num as IntegerWithRadix
|
||||
num.toString
|
||||
}
|
||||
}
|
||||
|
||||
def String effectiveValue(InstancePropertyRef ref){
|
||||
|
||||
}
|
||||
|
||||
def ComponentDefinition definingComponent(Instantiation instantiation){
|
||||
if(instantiation.componentRef!==null) instantiation.componentRef else instantiation.component
|
||||
}
|
||||
|
||||
def String generateHeader()
|
||||
|
||||
def String generateSource()
|
||||
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
package com.minres.rdl.generator
|
||||
|
||||
import com.minres.rdl.IntegerWithRadix
|
||||
import com.minres.rdl.rdl.ComponentDefinition
|
||||
import com.minres.rdl.rdl.ComponentDefinitionType
|
||||
import com.minres.rdl.rdl.ComponentInstance
|
||||
import com.minres.rdl.rdl.Instantiation
|
||||
import java.util.Date
|
||||
|
||||
class RegfileGenerator extends RdlBaseGenerator{
|
||||
|
||||
val ComponentDefinition componentDefinition
|
||||
|
||||
new(ComponentDefinition definition) {
|
||||
componentDefinition=definition
|
||||
}
|
||||
|
||||
override String generateHeader()'''
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017, MINRES Technologies GmbH
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Created on: «new Date»
|
||||
// * «componentDefinition.name».h Author: <RDL Generator>
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _«componentDefinition.name.toUpperCase»_H_
|
||||
#define _«componentDefinition.name.toUpperCase»_H_
|
||||
|
||||
#include <sysc/utilities.h>
|
||||
#include <util/bit_field.h>
|
||||
#include <sysc/register.h>
|
||||
#include <sysc/tlm_target.h>
|
||||
|
||||
namespace sysc {
|
||||
|
||||
class «componentDefinition.name» :
|
||||
public sc_core::sc_module,
|
||||
public sysc::resetable
|
||||
{
|
||||
protected:
|
||||
// storage declarations
|
||||
«FOR cdef : componentDefinition.componentDefinitions»
|
||||
«IF cdef.type == ComponentDefinitionType.REG»
|
||||
BEGIN_BF_DECL(«cdef.effectiveName»+'_t'», uint«cdef»_t);
|
||||
«cdef.genFieldDeclarations»
|
||||
END_BF_DECL();
|
||||
«ENDIF»
|
||||
«ENDFOR»
|
||||
«FOR instantiation : componentDefinition.instantiations»
|
||||
«IF instantiation.componentRef !==null && instantiation.componentRef.type == ComponentDefinitionType.REG»
|
||||
«instantiation.componentRef.effectiveName»+'_t' «instantiation.componentInstances.map[it.name].join(', ')»;
|
||||
«ENDIF»
|
||||
«IF instantiation.component !== null && instantiation.component.type == ComponentDefinitionType.REG»
|
||||
«IF instantiation.isFilledByField»
|
||||
uint«instantiation.size»_t «instantiation.componentInstances.map['r_'+it.name].join(', ')»;
|
||||
«ENDIF»
|
||||
«IF !instantiation.isFilledByField»
|
||||
BEGIN_BF_DECL(«instantiation.component.effectiveName»_t, uint«instantiation.size»_t);
|
||||
«instantiation.definingComponent.genFieldDeclarations»
|
||||
END_BF_DECL() «instantiation.componentInstances.map['r_'+it.name].join(', ')»;
|
||||
«ENDIF»
|
||||
|
||||
«ENDIF»
|
||||
«ENDFOR»
|
||||
// register declarations
|
||||
«FOR instantiation : componentDefinition.instantiations»
|
||||
«FOR instance : instantiation.componentInstances»
|
||||
«IF instantiation.isFilledByField»
|
||||
sysc::sc_register<uint«instantiation.size»_t> «instance.name»;
|
||||
«ENDIF»
|
||||
«IF !instantiation.isFilledByField»
|
||||
sysc::sc_register<typename «instantiation.component.effectiveName»_t::StorageType> «instance.name»;
|
||||
«ENDIF»
|
||||
«ENDFOR»
|
||||
«ENDFOR»
|
||||
|
||||
public:
|
||||
«componentDefinition.name»(sc_core::sc_module_name nm);
|
||||
|
||||
template<unsigned BUSWIDTH=32>
|
||||
void registerResources(sysc::tlm_target<BUSWIDTH>& target);
|
||||
};
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// member functions
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline sysc::«componentDefinition.name»::«componentDefinition.name»(sc_core::sc_module_name nm)
|
||||
: sc_core::sc_module(nm)
|
||||
«FOR instantiation : componentDefinition.instantiations»
|
||||
«FOR instance : instantiation.componentInstances»
|
||||
, NAMED(«instance.name», r_«instance.name», 0, *this)
|
||||
«ENDFOR»
|
||||
«ENDFOR»
|
||||
{
|
||||
}
|
||||
|
||||
template<unsigned BUSWIDTH>
|
||||
inline void sysc::«componentDefinition.name»::registerResources(sysc::tlm_target<BUSWIDTH>& target) {
|
||||
«FOR instantiation : componentDefinition.instantiations»
|
||||
«FOR instance : instantiation.componentInstances»
|
||||
target.addResource(«instance.name», 0x«Long.toHexString((instance.address as IntegerWithRadix).value)»UL, 0x«Long.toHexString((instantiation.size+7)/8)»UL);
|
||||
«ENDFOR»
|
||||
«ENDFOR»
|
||||
}
|
||||
|
||||
#endif // _«componentDefinition.name.toUpperCase»_H_
|
||||
'''
|
||||
|
||||
def boolean isFilledByField(Instantiation instantiation){
|
||||
val fieldCount = instantiation.component.instanceCountOfType(ComponentDefinitionType.FIELD)
|
||||
if(fieldCount==1) {
|
||||
val instSize=instantiation.size
|
||||
val field = instantiation.component.instantiationsOfType(ComponentDefinitionType.FIELD).get(0)
|
||||
val inst = field.componentInstances.get(0)
|
||||
val range = inst.range
|
||||
if(range===null)
|
||||
return instSize==field.size
|
||||
if(range.size !== null)
|
||||
return instSize==(range.size as IntegerWithRadix).value
|
||||
else {
|
||||
val left=(range.left as IntegerWithRadix).value
|
||||
val right=(range.right as IntegerWithRadix).value
|
||||
val size = if(left>right) left-right+1 else right-left+1
|
||||
return instSize==size
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
def int instanceCountOfType(ComponentDefinition definition, ComponentDefinitionType type){
|
||||
definition.instantiationsOfType(type).map[it.componentInstances.size].reduce[p1, p2|p1+p1]
|
||||
}
|
||||
|
||||
def instantiationsOfType(ComponentDefinition definition, ComponentDefinitionType type){
|
||||
definition.instantiations.filter[it.definingComponent.type == type]
|
||||
}
|
||||
|
||||
override generateSource() {
|
||||
''
|
||||
}
|
||||
|
||||
def String genFieldDeclarations(ComponentDefinition componentDef){
|
||||
var i=0L;
|
||||
var res = ""
|
||||
for( Instantiation inst: componentDef.instantiations){
|
||||
for(ComponentInstance compInst : inst.componentInstances){
|
||||
if(compInst.range.size!==null){
|
||||
res+='''BF_FIELD(«compInst.name», «i», «(compInst.range.size as IntegerWithRadix).value»);
|
||||
'''
|
||||
i+=(compInst.range.size as IntegerWithRadix).value
|
||||
} else {
|
||||
val start =(compInst.range.left as IntegerWithRadix).value
|
||||
val end = (compInst.range.right as IntegerWithRadix).value
|
||||
res+='''BF_FIELD(«compInst.name», «end», «start-end+1»);
|
||||
'''
|
||||
i=Math.max(start, end)+1
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user