精华内容
下载资源
问答
  • 2018-05-07 19:10:38

    本文,简单介绍下,如何运行hadoop自带的mapreduce的那些例子。

    本文针对的hadoop版本,是2.6.5版本,自带的例子包名为:hadoop-mapreduce-examples-2.6.5.jar;

    位于/share/hadoop/mapreduce目录下。

    简单来说,如果想要完成范例的运行,直接:

    hadoop jar hadoop-mapreduce-examples-2.6.5.jar wordcount /input /output

    其中,hadoop命令配置了环境变量。

    那么,这个执行具体是怎么实现的呢;或者说,如果我使用其他的名称,或者说WordCount首字母大写,因为解压缩该包能够看到WordCount.class文件的存在,能成功么?

    答案是:不能。

    为什么不能?

    我们仔细分析下这个整体的运行过程,目标在于,我们能够实现在mapreduce-examples包内添加我们自己的类,而且能够实现自己的运行逻辑。

    执行命令很简单,入口,看下hadoop的运行脚本:

    elif [ "$COMMAND" = "jar" ] ; then
          CLASS=org.apache.hadoop.util.RunJar

    很清楚,接下来需要注意的类,是RunJar类,而且指明了该类的位置,该命令,实际执行了RunJar中的main方法,我们看一下:

    /**
    	 * Run a Hadoop job jar. If the main class is not in the jar's manifest,
    	 * then it must be provided on the command line.
    	 */
    	public static void main(String[] args) throws Throwable {
    		new RunJar().run(args);
    	}

    这里看的更清楚了,我们在运行其它程序的时候,或者说是自己随意打出来的一个包,我们可以在后面直接指定className,就可以成功运行出结果了,注意,这里需要的className必须是全名。

    接下来,看看RunJar类中,run方法的内容:

    public void run(String[] args) throws Throwable {
    		String usage = "RunJar jarFile [mainClass] args...";
    		if (args.length < 1) {
    			System.err.println(usage);
    			System.exit(-1);
    		}
    
    		int firstArg = 0;
    		String fileName = args[firstArg++];
    		File file = new File(fileName);
    		if (!file.exists() || !file.isFile()) {
    			System.err.println("Not a valid JAR: " + file.getCanonicalPath());
    			System.exit(-1);
    		}
    		String mainClassName = null;
    		JarFile jarFile;
    		try {
    			jarFile = new JarFile(fileName);
    		} catch (IOException io) {
    			throw new IOException("Error opening job jar: " + fileName)
    					.initCause(io);
    		}
    		Manifest manifest = jarFile.getManifest();
    		if (manifest != null) {
    			mainClassName = manifest.getMainAttributes().getValue("Main-Class");
    		}
    		jarFile.close();
    
    		if (mainClassName == null) {
    			if (args.length < 2) {
    				System.err.println(usage);
    				System.exit(-1);
    			}
    			mainClassName = args[firstArg++];
    		}
    		mainClassName = mainClassName.replaceAll("/", ".");
    
    		File tmpDir = new File(System.getProperty("java.io.tmpdir"));
    		ensureDirectory(tmpDir);
    
    		final File workDir;
    		try {
    			workDir = File.createTempFile("hadoop-unjar", "", tmpDir);
    		} catch (IOException ioe) {
    			// If user has insufficient perms to write to tmpDir, default
    			// "Permission denied" message doesn't specify a filename.
    			System.err.println("Error creating temp dir in java.io.tmpdir "
    					+ tmpDir + " due to " + ioe.getMessage());
    			System.exit(-1);
    			return;
    		}
    
    		if (!workDir.delete()) {
    			System.err.println("Delete failed for " + workDir);
    			System.exit(-1);
    		}
    		ensureDirectory(workDir);
    		ShutdownHookManager.get().addShutdownHook(new Runnable() {
    			@Override
    			public void run() {
    				FileUtil.fullyDelete(workDir);
    			}
    		}, SHUTDOWN_HOOK_PRIORITY);
    
    		unJar(file, workDir);
    
    		ClassLoader loader = createClassLoader(file, workDir);
    
    		Thread.currentThread().setContextClassLoader(loader);
    		Class<?> mainClass = Class.forName(mainClassName, true, loader);
    		Method main = mainClass.getMethod("main", new Class[] { Array
    				.newInstance(String.class, 0).getClass() });
    		String[] newArgs = Arrays.asList(args).subList(firstArg, args.length)
    				.toArray(new String[0]);
    		try {
    			main.invoke(null, new Object[] { newArgs });
    		} catch (InvocationTargetException e) {
    			throw e.getTargetException();
    		}
    	}

    纵观全部的代码,很容易看懂,主要是解析我们的参数,然后将提交的包解压,其中需要注意这一句:

    Manifest manifest = jarFile.getManifest();
    		if (manifest != null) {
    			mainClassName = manifest.getMainAttributes().getValue("Main-Class");
    		}

    跟上文对应,如果打包的文件中有这个ManiFest文件,且包含了Main-class的配置,那么,就会运行这个Main-class的main方法。

    Class<?> mainClass = Class.forName(mainClassName, true, loader);
    		Method main = mainClass.getMethod("main", new Class[] { Array
    				.newInstance(String.class, 0).getClass() });

    最后的运行逻辑如下:

    try {
    			main.invoke(null, new Object[] { newArgs });
    		} catch (InvocationTargetException e) {
    			throw e.getTargetException();
    		}

    可以看出,使用反射机制来运行,暂且不提,按照这一段的说法,运行的是MAINFEST内定义的Main-class,那么,我们看下,包里面是否有MAINFEST文件呢?

    有的,对于2.6.5版本的hadoop来说,文件内容如下:

    Manifest-Version: 1.0
    Archiver-Version: Plexus Archiver
    Created-By: Apache Maven
    Built-By: YYZYHC
    Build-Jdk: 1.8.0_131
    Main-Class: org.apache.hadoop.examples.ExampleDriver

    非常清楚,接下来我们需要看一下,这个ExampleDriver是何方神圣:

    public class ExampleDriver {
    	public static void main(String argv[]) {
    		int exitCode = -1;
    		ProgramDriver pgd = new ProgramDriver();
    		try {
    			pgd.addClass("wordcount", WordCount.class,
    					"A map/reduce program that counts the words in the input files.");
    			pgd.addClass(
    					"wordmean",
    					WordMean.class,
    					"A map/reduce program that counts the average length of the words in the input files.");
    			pgd.addClass(
    					"wordmedian",
    					WordMedian.class,
    					"A map/reduce program that counts the median length of the words in the input files.");
    			pgd.addClass(
    					"wordstandarddeviation",
    					WordStandardDeviation.class,
    					"A map/reduce program that counts the standard deviation of the length of the words in the input files.");
    			pgd.addClass(
    					"aggregatewordcount",
    					AggregateWordCount.class,
    					"An Aggregate based map/reduce program that counts the words in the input files.");
    			pgd.addClass(
    					"aggregatewordhist",
    					AggregateWordHistogram.class,
    					"An Aggregate based map/reduce program that computes the histogram of the words in the input files.");
    			pgd.addClass("grep", Grep.class,
    					"A map/reduce program that counts the matches of a regex in the input.");
    			pgd.addClass("randomwriter", RandomWriter.class,
    					"A map/reduce program that writes 10GB of random data per node.");
    			pgd.addClass("randomtextwriter", RandomTextWriter.class,
    					"A map/reduce program that writes 10GB of random textual data per node.");
    			pgd.addClass("sort", Sort.class,
    					"A map/reduce program that sorts the data written by the random writer.");
    
    			pgd.addClass("pi", QuasiMonteCarlo.class,
    					QuasiMonteCarlo.DESCRIPTION);
    			pgd.addClass("bbp", BaileyBorweinPlouffe.class,
    					BaileyBorweinPlouffe.DESCRIPTION);
    			pgd.addClass("distbbp", DistBbp.class, DistBbp.DESCRIPTION);
    
    			pgd.addClass("pentomino", DistributedPentomino.class,
    					"A map/reduce tile laying program to find solutions to pentomino problems.");
    			pgd.addClass("secondarysort", SecondarySort.class,
    					"An example defining a secondary sort to the reduce.");
    			pgd.addClass("sudoku", Sudoku.class, "A sudoku solver.");
    			pgd.addClass("join", Join.class,
    					"A job that effects a join over sorted, equally partitioned datasets");
    			pgd.addClass("multifilewc", MultiFileWordCount.class,
    					"A job that counts words from several files.");
    			pgd.addClass("dbcount", DBCountPageView.class,
    					"An example job that count the pageview counts from a database.");
    			pgd.addClass("teragen", TeraGen.class,
    					"Generate data for the terasort");
    			pgd.addClass("terasort", TeraSort.class, "Run the terasort");
    			pgd.addClass("teravalidate", TeraValidate.class,
    					"Checking results of terasort");
    			exitCode = pgd.run(argv);
    		} catch (Throwable e) {
    			e.printStackTrace();
    		}
    		System.exit(exitCode);
    	}
    }

    这代码也很容易看懂,里面定义了一个ProgramDriver,这个类位于hadoop-common包下,具体内容不贴了,接下来介绍其中部分被ExampleDriver使用到的东西:

    /**
    	 * A description of a program based on its class and a human-readable
    	 * description.
    	 */
    	Map<String, ProgramDescription> programs;
    
    	public ProgramDriver() {
    		programs = new TreeMap<String, ProgramDescription>();
    	}

    只有一个构造器,生成了一个TreeMap,key是String,value是ProgramDescription,其内没什么花样,主要是一个main的Method,一个String的description。

    ExampleDriver中,ProgramDriver中添加了很多的class,最后执行了run方法,所以,接下来我们看下ProgramDriver中的run方法:

    /**
    	 * This is a driver for the example programs. It looks at the first command
    	 * line argument and tries to find an example program with that name. If it
    	 * is found, it calls the main method in that class with the rest of the
    	 * command line arguments.
    	 * 
    	 * @param args
    	 *            The argument from the user. args[0] is the command to run.
    	 * @return -1 on error, 0 on success
    	 * @throws NoSuchMethodException
    	 * @throws SecurityException
    	 * @throws IllegalAccessException
    	 * @throws IllegalArgumentException
    	 * @throws Throwable
    	 *             Anything thrown by the example program's main
    	 */
    	public int run(String[] args) throws Throwable {
    		// Make sure they gave us a program name.
    		if (args.length == 0) {
    			System.out.println("An example program must be given as the"
    					+ " first argument.");
    			printUsage(programs);
    			return -1;
    		}
    
    		// And that it is good.
    		ProgramDescription pgm = programs.get(args[0]);
    		if (pgm == null) {
    			System.out.println("Unknown program '" + args[0] + "' chosen.");
    			printUsage(programs);
    			return -1;
    		}
    
    		// Remove the leading argument and call main
    		String[] new_args = new String[args.length - 1];
    		for (int i = 1; i < args.length; ++i) {
    			new_args[i - 1] = args[i];
    		}
    		pgm.invoke(new_args);
    		return 0;
    	}

    这里,看的很清楚,先根据后续的参数,提取出对应的类,然后予以执行。

    同样,我们定义的wordcount实质上就是上文中TreeMap的key,必须精确指定,才能让程序运行起来。

    综上所述,运行Hadoop自带的MapReduce-examples的途径有以下几种:

    1. 修改打包的实现,删除MAINFEST中的Main-class,或者是修改Main-class的名称为自己想要的名称。
    2. 直接指定自己的className全名,简易便捷。
    更多相关内容
  • 依据XML定义报文格式解析数据

    万次阅读 2017-07-20 09:25:30
    1、将报文格式定义在xml中 2、通过格式xml来解析数据 当前体会到此设计的优势: (1)报文格式可在xml中配置。报文格式调整不涉及程序调整。 (2)程序可读性强。实现: 一、自定义xml文件 二、读取XML...

    需求:
    报文(数据集)中含有多个字段,要解析为多个含有意义的字段。
    设计:
    1、将报文格式定义在xml中
    2、通过格式xml来解析数据
    当前体会到此设计的优势:
    1、报文格式可在xml中配置。报文格式调整不涉及程序调整。
    2、程序可读性强。

    实现:
    一、自定义xml文件
    1、节点标签
    2、子节点标签
    3、数据标签
    4、描述标签
    5、起始下标
    6、长度标签
    example.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <infos>
    <!-- 数据截取模板定义 -->
    <info>     
       <dataName>data1</dataName>  
       <desc>数据1</desc>   
       <startIndex>6</startIndex>
       <length>10</length>  
    </info>
    <infos>

    二、读取XML文件解析
    1、读取xml,获取Document。

     public static Document getDocument(Class<?> cls, String fileName) throws Exception{
        InputStream in = null;        
         try{
            //把要解析的XML文档转化为输入流,方便DOM解析器解析
            in = cls.getClassLoader().getResourceAsStream(fileName);
            //得到DOM解析器的工厂实例
            //从DOM工厂获得DOM解析器   
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();   
            return builder.parse(in);
        }finally{
            if(in != null){
                in.close();
            }
        }
        return null;
    }
    补充:
    (1)导入包org.w3c.dom.Document
       a. dom,为文档对象模型 (DOM) 提供接口,该模型是 Java API for XML Processing 的组件 API。
       具体详见:http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/org/w3c/dom/package-summary.html。
       b. Document 接口表示整个HTML、XML。
       具体详见:http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/org/w3c/dom/Document.html。
       另:
       a. javax.swing.text 提供类 HTMLEditorKit 和创建 HTML 文本编辑器的支持类。类相关说明详见:http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/javax/swing/text/package-use.html#javax.swing.text
       b. javax.swing.text.Document 作为swing文本组建模型的文本容器。
     (2)得到XML文档的根节点 
         Element rootNode = document.getDocumentElement();
     (3)从根节点获取子节点
         NodeList nodes = rootNode.getChildNodes();
     (4)DOM Document Object Model,也即文档对象模型。
         对xml文件进阶操作,用Java W3C DOM 进行XML操作的例子,可详见:
         http://blog.csdn.net/cds27/article/details/2139110。
     (5)从xml设置到Map
         注意:应在类中静态块中,进行初始化。
    
    static{          
                   setSubTemplateToMap();
               }
    
         private static final String SUBTEMPLATEFILENAME = "example.xml";
         private static Map subTemplateMap = new HashMap<String, List<Map<String,String>>>();
         public static void setSubTemplateToMap(){
            try {
                Document document = XmlDocumentUtils.getDocument(SUBTEMPLATEFILENAME );
                NodeList nodeList = document.getElementsByTagName("info");
                List<Map<String,String>> list = new ArrayList<Map<String,String>>();
                for ( int i = 0; i < nodeList.getLength(); i++){
                     Map<String,String> result = new HashMap<String, String>();
                     String dataName = document.getElementsByTagName("dataName").item(i).getFirstChild().getNodeValue();
                     String startIndex = document.getElementsByTagName("startIndex").item(i).getFirstChild().getNodeValue();
                     String length = document.getElementsByTagName("length").item(i).getFirstChild().getNodeValue();
                     if(StringUtils.empty(dataName)){
                         throw new Exception("第["+i+"]个"+"dataName"+"标签的内容不能为空");
                     }
                     if ( StringUtils.empty(startIndex)){
                         throw new Exception("第["+i+"]个"+"startIndex"+"标签的内容不能为空");
                     }
                     if ( StringUtils.empty(length)){
                         throw new Exception("第["+i+"]个"+"length"+"标签的内容不能为空");
                     }
                     result.put("dataName", dataName);
                     result.put("startIndex", startIndex);
                     result.put("length", length);
                     list.add(result);
                }
                subTemplateMap.put(SUBTEMPLATEFILENAME.substring(0,SUBTEMPLATEFILENAME.lastIndexOf(".")), list);
            } catch ( Throwable e ) {
                e.printStackTrace();
            }
        } 
    
        /*解析数据*/
         public static boolean getStrByTemplateMap(String data,String templateName,Map<String,String> result){
            boolean bool = false;
            List<Map<String,String>> list = subTemplateMap.get(templateName);//获取数据匹配模板
            if(list != null && list.size()>0){
                for(Map<String,String> map : list){
                    try{
                            String dataName = map.get("dataName");
                            int startIndex = Integer.parseInt(map.get("startIndex"));
                            int length = Integer.parseInt(map.get("length"));
                            String subData = data.substring(startIndex,startIndex+length).trim();
                            result.put(dataName, subData);
                    }catch(Throwable e){
                        //logger.error("通过模板解析数据异常",e);
                        if(!bool){
                            bool = true;
                        }
                    }
                }
            }
            return bool;
        }
    补充:
    a. NodeList getElementsByTagName(String tagName) Document成员方法
       按文档顺序返回包含在文档中且具有给定标记名称的所有 Element 的 NodeList。
    b. int getLength() NodeList类成员方法 获取NodeList的长度,即Node数目。
    c. Node item(int index) NodeList类成员方法 返回集合中第index个项。
       NodeList详见:http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/org/w3c/dom/NodeList.html。
    d. Node getFirstChild() 此节点的第一个子节点。
    e. String getNodeValue()此节点的值。
    
    注意:
    a. xml文件要能读取到,可放置在同一目录下。
    b. 若无日志操作的,可注释掉logger部分。
    c. 引用的工具类StringUtils,可将其替换为 字符串判NULL或判空字符串。
    
    展开全文
  • 语法解析及Antlr

    千次阅读 2020-02-11 19:28:00
    1 语法解析 1.1 语法解析器 1.1.1 执行流程 1.1.2 语法树好处 1.1.3 解析方法LL与LR 1.1.4 抽象语法树(AST) 1.2 语法规则文件 2 Antlr 2.1 解析方法 2.1.1 递归下降语法分析器 2.1.2 ALL(*)解析器 2.2 ...

    目录

     

    1 语法解析

    1.1 语法解析器

    1.1.1 执行流程

    1.1.2  语法树好处

    1.1.3 解析方法LL与LR

    1.1.4 抽象语法树(AST)

    1.2  语法规则文件

    2 Antlr

    2.1  解析方法

    2.1.1  递归下降语法分析器

    2.1.2 ALL(*)解析器

    2.2  语法解析树

    2.3 使用方法

    2.3.1 使用流程

    3 Listener 和 Visitor 机制

    3.1 Listener模式

    3.2 Visitor模式

    3.3 相关问题释疑

    3.9 实践问题

    4 规则命中策略

    5 antlr的应用

    5.1 在Twitter中的应用

    5.2  大数据产品中的应用 

    9 参考资料


    1 语法解析

    1.1 语法解析器

    1.1.1 执行流程

    第一个阶段是词法分析,主要负责将符号文本分组成符号类tokens

    第二个阶段就是真正的语法解析,目标就是构建一个语法解析树。

     

    语法解析的输入是tokens,输出就是一颗语法解析树。

    1.1.2  语法树好处

    1. 树形结构易于遍历和处理,并且容易被程序员理解,方便了应用代码做进一步处理。

    2. 多种解释或翻译的应用代码都可以重用一个解析器。但 ANTLR 也支持像传统解析器生成器那样,将应用处理代码直接嵌入到语法中。

    3. 对于因为计算依赖而需要多趟处理的翻译器来说,比起多次调用解析器去解析,遍历语法树多次更加高效。

    1.1.3 解析方法LL与LR

    根据推导策略的不同,语法解析分为LL与LR两种

    LR自低向上(bottom-up)的语法分析方法

    LL是自顶向下(top-down)的语法分析方法

    举个例子,比如如何描述一个Json字符串?

    Json字符串是一个key:Value字符串,其中key是字符串,value可以是String、数字、或者又是一个Json。csv也是这样,类似一种递归定义。

    exp1 = x1;

    exp2 = x2;

    result = exp1 'operator' exp2

    两类分析器各有其优势,适用不同的场景,很难说谁要更好一些。普遍的说法是LR可以解析的语法形式更多LL的语法定义更简单易懂

    1.1.4 抽象语法树(AST)

     抽象语法树是将源代码根据其语法结构,省略一些细节(比如:括号没有生成节点),抽象成树形表达。

     抽象语法树在计算机科学中有很多应用,比如编译器、IDE、压缩优化代码等。

     如配置文件读取器,遗留代码转换器,wiki 标记渲染器和 JSON 解析器

     [抽象语法树] https://tech.meituan.com/abstract-syntax-tree.html 该文中也举了一个遗留代码转换器的例子

     Json解析器:Twitter

     note:一些比较简单的其实可以用正则来处理,但是规则容易误中,表达能力也非常受限。

    源文件--->抽象,结构化------->parse

    1.2  语法规则文件

    编写语法规则文件的同时,其实就是一个抽象的过程。示例如下:

     

    从上图可以看出主要语法文件分为四块,第二块一般可以不写,最重要的就是语法规则。

    语法规则是以小写字母组成。如prog,stat。词法规则由大写字母组成。如ID:[a-zA-Z]+。

    通过使用 | 运算符来将不同的规则分割,还可以使用括号构成子规则。例如(‘*’ | ‘/’)会匹配多个乘号或除号。注释: // /* */其他空格连接符: 表示顺序连接| 选择符: 表示或者的关系重复次数: + *可选: ?

     

    https://github.com/antlr/grammars-v4 这类有各种语法文件示例。

    FAQ

    Q: 为啥大家都是换行写分号?这个是

    A: 【TODO】

    Q: stat+ 是必须的表示语句一次可以有多个吗?

    A:表示可以一次解析多个语句。比如 ANTLRInputStream inputStream = new ANTLRInputStream("ord=(3*4)/2 price=(4*3)/2"); 这两个stat就可以解析。当然如果我们是每次只解析一个,也可以不写+。

    Q: 那么多规则,规则内还有分支,谁先谁后?具体的逻辑。

    A: 这个谁先谁后还是看具体场景的逻辑,整体就是希望先被识别成哪种模式,就把该标签放在前面。

    2 Antlr

    2.1  解析方法

    2.1.1  递归下降语法分析器

    是自顶向下语法解析器的一种。

    顾名思义,递归下降指的就是解析过程是从语法树的根开始,向叶子(token)递归。还是以前面的赋值表达式解析为例,其递归下降语法分析器的代码大概是下面这个样子:

     

     

    stat()、assign()、expr()等方法调用所形成的调用栈能与语法分析树的内部节点一一对应。match()的调用对应树的叶子,而assign()方法直接顺序读取输入字符,而不用做任何选择。相比之下,stat()方法要复杂一些,因为在解析时,它需要向前看一些字符才能确认走哪个代码分支,有时甚至要读取完所有输入才能得出预测结果。

    2.1.2 ALL(*)解析器

    ANTLR从4.0开始生成的是ALL(*)解析器,其中A是自适应(Adaptive)的意思。对传统的LL(*)解析器有很大的改进,ANTLR是目前唯一可以生成ALL(*)解析器的工具。

    2.2  语法解析树

    在ANTLR4中已经不再提供生成AST的功能(在ANTLR3中可以通过Options指定output=AST来生成AST),而是生成了分析树(Parse Tree)。

    2.3 使用方法

    2.3.1 使用流程

    http://www.antlr.org/

    自定义文法--->词法分析器(Lexical)------>语法分析器(Parse)—→Grammer

    draw.io evaluation version

    Lexical

    文法文件

    Parse

    处理后的文件

    输入

    g4文件

    Parse

    输出

    antlr

    1. 自定义词法分析器 (g4后缀)文件

    2. 用工具生成Java文件

    3. 继承xxBaseVisitor类实现自己的Visitor

     

    在整个Antlr的使用过程中,最重要的就是编写规则文件。

    规则文件的编写:

    Grammer文件:

    grammar Cal;

    prog: stat+;  //一个程序由至少一条语句组成

    /*为了以后的运算的方便性,我们需要给每一步规则打上标签,标签以”#”开头,出现在每一条规则的右边。打上标签后,antlr会为每一个规则都生成一个事件;在没有标签的情况下,每个规则会生成一个Context以及一个事件。 如下述语句,如果没有标签,那么只会生成一个StatContext和一个visitStat方法。 */

    stat: ID '=' expr ';' #Assign //变量赋值语句

    | 'print' '(' expr ')' ';' #printExpr   //输出语句

    ;

    expr: expr op=('*'|'/') expr #MulDiv  //表达式可以是表达式之间乘除

    | expr op=('+'|'-') expr #AddSub  //表达式可以是表达式之间加减

    | NUM #NUM    //表达式可以是一个数字

    | ID #ID  //表达式可以是一个变脸

    | '(' expr ')' #parens    //表达式可以被括号括起来

    ;

    MUL:'*';

    DIV:'/';

    ADD:'+';

    SUB:'-';

    ID: [a-zA-Z][a-zA-Z0-9]*; //变量可以是数字和字母,但必须以字母开头

    //负数必须要用"()"括起来

    NUM: [0-9]+   //正整数

    | '(' '-' [0-9]+ ')'  //负整数

    | [0-9]+'.'[0-9]+   //正浮点数

    | '(' '-' [0-9]+'.'[0-9]+ ')'   //负浮点数

    ;

    WS: [ \t\r\n] -> skip;    //跳过空格、制表符、回车、换行

    生成命令:

    编辑完 Antlr 文件后,我们在安装有 Antlr plugin 的 Intellij 上,可以通过右键语法规则对语法规则进行测试,并可以在配置生成中间代码的包名、路径等选项后,直接生成中间代码。

     

    生成的文件说明:

     

     

    • Hello.tokens和HelloLexver.tokens为文法中用到的各种符号做了数字化编号,对于我们定义的每个 token,ANTLR 分配了一个 token 类型码并将这些值保存在 ArrayInit.tokens。因为这个文件的存在,当我们将较大规模的语法分割为各种小型的语法表达时,ANTLR 能够使同种 token 的类型码保持一致。

    • HelloLexer.java 包含专用的词法分析程序(lexer)类的定义

    • HelloParse.java 包含了专用于 ArrayInit 语法的解析器(parser)类的定义。

    • HelloVisitor 和 HelloBaseVisitor 分别是语法解析树的vistor的接口和类,用于遍历整个语法树。一般情况下,我们通过继承 HelloBaseVisitor 来实现自己对于语法树遍历的处理。

    • HelloListener和HelloBaseListener是用listen方式来遍历树的解析方法。

     

    HelloParse说明:

    ANTLR为每个子规则创建一个同名函数,因此可以方便地取到其子规则的context。即每个规则对应一个Context。

     

    Context对象包含以下内容:

    语法分析时生成

    • 起始Token,终止Token

    • children: 可以得到子语法规则中的内容。

    • 异常信息: 可以得到解析失败的信息。

     

     

     

    查看语法解析树:

     

    3 Listener 和 Visitor 机制

    在ANTLR 4以前,有两种开发方式:一是将目标语言的代码直接硬编码到语法定义文件中,在生成分析器时会插入这些代码到生成文件中,这也是大多数语法分析器生成工具的做法。

    在上边的语法判定与通道的例子中,就有将Java代码硬编码到语法定义的情况。将目标代码和语法定义耦合在了一起,当需要生成不同目标语言的分析器时,就需要维护多份语法定义文件,

    ‘增加了维护成本,同时在编写复杂逻辑时,由于IDE没有对目标语言的支持,开发和测试都很幸苦。另一种方式是让ANTLR生成语法分析树,然后写程序遍历语法树,对语法树的遍历是一个很复杂的工作。

    ANTLR 4开始会生成监听器(Listener)与访问者(Visitor),将语法定义与目标代码完全的解耦。监听器可以被动的接受语法分析树遍历的事件,对于每一个语法节点,

    都会生成进入enterSomeNodeName与退出exitSomeNodeName两个方法。访问者机制生成对语法节点的访问方法visitSomeNodeName,在访问方法中需要手动调用visit方法来对子节点进行遍历,

    使用访问者模式可以控制语法树的遍历,略过某些的分枝。ANTLR默认只生成监听器,生成访问者类需要在生成时添加-visitor选项。

     Visitor和Listener是antlr提供的两种树遍历机制。Listener是默认的机制,可以被antlr提供的遍历器对象调用;如果要用Visitor机制,在生成识别器时就需要显式说明 

    antlr4 -no-listener -visitor Calc.g4,并且必须显示的调用visitor方法遍历它们的子节点,在一个节点的子节点上如果忘记调用visit方法,就意味着那些子数没有得到访问

    核心区别:两者各有优劣,Listener模式适合全局查找,默认是深度优先遍历,而Visitor模式适合指定某个节点作遍历。

    listener默认是遍历每个节点,对于每个节点都有enter和exit事件。对于visitor则是需要自己手动用写代码去进行指定节点遍历。

    3.1 Listener模式

    示例如下

    public class WlgsParseListener extends MerakDslBaseListener{
        private ParseTreeProperty<String> property = new ParseTreeProperty<>();
    
        @Override
        public void exitArithmeticBinary(MerakDslParser.ArithmeticBinaryContext ctx) {
            MerakDslParser.ExpressionContext right = ctx.right;
            MerakDslParser.ExpressionContext left = ctx.left;
            String formula = left.getText() +  ctx.operator.getText() + right.getText();
            property.put(ctx,formula);
            super.exitArithmeticBinary(ctx);
        }
    }

    Listener模式的exitXX方法的返回值都是空,在遍历过程所有有临时数据需要保存,可以存近一个自定义数据结构中或者使用antrl的ParseTreeProperty中或者自定义一个数据结果保存都是OK的。

    3.2 Visitor模式

    从上面可以看出,即使我们不使用super调用父类的同名方法,也不调用自定义baseListener中的其他方法,其实listen也是可以完成遍历的。因此listener是被动进行遍历的。

    visitor不一样,是手动进行遍历,必须手动使用visit调用其他的visitor方法,或者使用super,父类中该方法是调用所有的子类visitor。

    从以下源码可以看出,super.visitXX都是一样的,都是visit 参数Context的 Children。

    @Override public T visitAssignment(MerakDslParser.AssignmentContext ctx) { return visitChildren(ctx); }
    	/**
    	 * {@inheritDoc}
    	 *
    	 * <p>The default implementation returns the result of calling
    	 * {@link #visitChildren} on {@code ctx}.</p>
    	 */
    	@Override public T visitCaseWhen(MerakDslParser.CaseWhenContext ctx) { return visitChildren(ctx); }
    	/**

    整体流程就是,

    visitProg(){
    
    super.visitProg(ctx)  //visit children就是所有的stat.accept(visitor)
      stat.accept(visitor) ==> visitor.visitStat()
      visitStat(ctx){super.visitStat} // visitStat的child
      以此类推。。
    }

    Q:如果Prog下有多个stat,那么用super visit所有的children的结果如何合并为一个结果(该结果是visitStat的返回值)。

    A: visitChildren源码可以看出,多个child的结果是,后续的结果会覆盖前面的结果,也就是visitProg的返回值就是最后一个visitStat的结果。

        public T visitChildren(RuleNode node) {
            T result = this.defaultResult();
            int n = node.getChildCount();
    
            for(int i = 0; i < n && this.shouldVisitNextChild(node, result); ++i) {
                ParseTree c = node.getChild(i);
                T childResult = c.accept(this);
                result = this.aggregateResult(result, childResult);
            }
    
            return result;
        }

    比如对于(3*4)/2 ;方法visitParens,如果方法实现是使用super.visitParens,则会返回")"。 Parens的children数组元素有三个,(、exp、);最后的“)”会覆盖前面两个。这个时候我们自定义visit的作用就体现出来了,一般对于四则运算的visitParens我们会重写如下

    visit(ctx.expression());

    重写的visitParens中,我们就手动指定了节点的访问。因此可以认为visitor模式就是主动遍历的模式。

    接下来还需要明确下 Context的层级结构,比如

     

     

    3.3 相关问题释疑

    1 语法文件中的#意味着什么?和生成的Context结构如何对应?

    #号的含义在上文语法文件说明部分已经解释了,没有#规则名对应一个Context,有#能以更细的分支来管理规则,#后面的别名对应着一个Context,也对应着一个访问方法。

    2 大部分时候都是有children,到底什么节点下有children?

     

    目前这个Expression的children下面没有数组,而是left 和right的属性。

    因此猜测,凡是有 left = xx;有这种描述语句的时候都是没有数组的。这个时候不能使用 super.xxx进行遍历

     

    3.9 实践问题

    其他细节:因为 字符串和null用+号拼接的时候,就相当于字符串 + “null”因此可能会出现 (null)的现象。有两种办法,一种在遍历括号节点的时候加判断,或者解析公式之前就加个校验,保证不出出现这种问题,

    天璇V3Manager工程 物理公式解析就是前端控件进行了校验,保证了不会出现这个问题。

    if (ctx.expression() != null){ 
      return visitExpression(ctx.expression());             
    } else{ 
      return Double.parseDouble(ctx.INT().getText());             
    }

    使用 Maven

    Antlr4 提供了 Maven Plugin,可以通过配置来进行编译。语法文件 g4 放置在 src/main/antlr4 目录下即可,配置依赖的 antlr4 和 plugin 即可。生成 visitor 在 plugin 配置 visitor 参数为 true 即可。

    注意:antlr4 的库版本要与 plugin 版本对应,antlr4 对生成文件用的版本与库本身的版本会进行对照,不匹配会报错。

    ...
    <properties>
        <antlr4.version>4.7.2</antlr4.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.antlr</groupId>
            <artifactId>antlr4</artifactId>
            <version>${antlr4.version}</version>
        </dependency>
    </dependencies>
    ...
    <build>
        <plugins>
            <plugin>
                <groupId>org.antlr</groupId>
                <artifactId>antlr4-maven-plugin</artifactId>
                <version>${antlr4.version}</version>
                <configuration>
                    <visitor>true</visitor>
                </configuration>
                <executions>
                    <execution>
                        <id>antlr</id>
                        <goals>
                            <goal>antlr4</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    ...

     

    4 规则命中策略

    规则这么多,也许一个表达式能够命中多个规则分支,最终命中哪个是什么策略决定的。以下进行说明。

    【TODO】

    5 antlr的应用

    一般的处理对象有:

    • SQL语句

    • 算数表达式

    • 编程语言的源文件

    可以看出来其实编写grammar文件的过程其实就是对某种场景的抽象,定义一些基础的元素,然后Parse能够针对输入生成某种输入的树结构,然后遍历这种树结构,在遍历的过程中可以做一些处理,比如翻译。

     

    note:

    [各种语言的antlr解析] https://blog.csdn.net/xiyue_ruoxi/article/details/38925091 

    关于SQL的antlr解析

    5.1 在Twitter中的应用

    将查询参数解析成一个语法解析树

    5.2  大数据产品中的应用 

    antlr在hibernate、presto以及SparkSQL中都是使用了的。

    antlr在presto及SparkSQL中作用是相似的,都是就是生成了生成一个SQL语法解析树(一个未处理的逻辑执行计划)。

     

     

    后续是对逻辑执行计划是进行了优化的。

    也就是说presto和spark是从SQL到逻辑执行计划;天璇的逻辑执行计划是从查询参数构建出来,也就是手动完成了antlr的过程。

    9 参考资料

    1. [官网 看 antlr 4 reference手册,权威 必看http://www.antlr.org/

    2. [进阶] https://liangshuang.name/2017/08/20/antlr/

    3. [基础] http://kyonhuang.top/ANTLR-learning-notes-1/

    4.  [visitor] https://dohkoos.gitbooks.io/antlr4-short-course/content/calculator-visitor.html

    5. https://toutiao.io/posts/2a6f21/preview

    6. [使用语法错误异常做补全] https://www.liangshuang.name/2017/08/20/antlr/

    7. [各种语法文件] https://github.com/antlr/grammars-v4

    展开全文
  • 抽象代数——群的基本定义和一些例子

    万次阅读 多人点赞 2015-08-28 14:26:33
    群论的基本概念点较多,且各概念点之间关系纵横交错,学习起来颇有本科时初学线性代数时的感觉,觉得有必要整理一下,先梳理一下群的基本定义例子。 首先作几点说明: 1、群(group)、环(ring)、域(field)是抽象...

    群论的基本概念点较多,且各概念点之间关系纵横交错,学习起来颇有本科时初学线性代数时的感觉,觉得有必要整理一下,先梳理一下群的基本定义和例子。
    首先作几点说明:
    1、(group)、(ring)、(field)是抽象代数(abstract algebra)中基本的代数结构(algebraic structures)
    2、上述这些代数结构抽象代数(abstract algebra)的研究对象之一,另一个研究对象是通过研究这些代数结构间的保持运算的映射(态射(morphism))
    3、1872年,F.Klein在被聘为埃尔朗根大学的数学教授的就职演讲中阐述了几何学统一的思想:所谓几何学,就是研究几何图形对于某类变换群保持不变的性质的学问,或者说任何一种几何学只是研究与特定的变换群有关的不变量(《埃尔朗根纲领(Erlangen Program)》)

    〇、前置概念

    名称英文名称定义说明
    代数运算Algebra Calculation非空集合S与自己的笛卡尔积 S×S S 的一个映射 在群论中通常是指所谓的二元代数运算
    正交点变换 又称为保距变换(isometry)
    正交矩阵 Orthogonal Matrix AAT=ATA=E
    酉矩阵Unitary Matrix UUH=UHU=En 式中H为共轭转置

    这里写图片描述

    一、群的定义

    1、群的基本定义

    序号定义说明
    1代数运算定义了一个代数运算的非空几何
    2结合律 (ab)c=a(bc),a,b,cG
    3单位元存在律 sG,ea=ae=a,aG
    4逆元存在律 aG,bG,ab=e

    2、 群定义的衍生

    名称英文名称定义说明
    Group满足前述全部4条群的基本定义的非空集合
    半群Semigroup仅满足前述群的基本定义中的前2条的非空集合,即:
    1)定义了集合上的代数运算
    2)适用结合律
    但是,并不要求存在单位元和逆元
    也有地方称为仿群
    幺半群Monoid满足前述群的基本定义中的前3条的非空集合,即:
    1)定义了集合上的代数运算
    2)适用结合律
    3)存在单位元
    但是,并不要求存在逆元
    阿贝尔群Abel Group在满足前述全部4条群的基本定义的前提下,再补充一条:群元素满足交换律 ab=ba,a,bG

    二、群的例子

    1、生活中群的例子

    名称英文名称说明
    平面晶体群Plane Crystallographic Group又被称为贴墙纸群(Wallpaper Group)
    已经G.Polya在1924年完成对平面晶体群的分类:共有17种不同的平面晶体群
    空间晶体群Space Crystallographic GroupFedorov和Schonflies分别独立地证明了空间晶体群共有230个
    魔方群Rubik’s Cube grouphttps://en.wikipedia.org/wiki/Rubik%27s_Cube_group

    2、数集中群的例子

    名称符号定义说明
    整数加群
    实数加群
    n次单位根群 Un Un 的生成元成为复数域中的本原n次单位根(primitive n th root of unity)

    3、几何中群的例子

    中文名称英文名称符号定义说明
    欧几里得群Euclidean Group En n 维空间所有正交点变换的集合 E2 为平面欧氏群 E3 为空间欧氏群
    二面体群Dihedral Group Dn n 边形的对称(性)群,n3

    4、代数中群的例子

    中文名称英文名称符号定义说明
    模n剩余类环 Zm Zm=0,1,2,,m1 该群的生成元是 1¯(i¯=i1¯)
    Zm 的单位群 Zm ’s Group of Units U(Zm)Zm Zm=0,1,2,,m1 该群的生成元是 1¯(i¯=i1¯)
    Zp 的乘法群 Zp 当m为素数p时, Zm 中所有非零元组成的集合对于乘法构成的一个abel群该群是一个abel群
    当m为素数时,根据欧拉定理 Zp 中的所有元素都有逆元(inverse unit)
    一般线性群General Linear Group GLn(F) 域F上所有n级可逆矩阵组成的集合,对于矩阵的乘法所成的群是矩阵群(Matrix Group)的一种
    特殊线性群Special Linear Group SLn(F) 在一般线性群定义的基础上再补充定义,所有的矩阵行列式为1是矩阵群(Matrix Group)的一种
    正交群Orthogonal Group On 实数域上所有n级正交矩阵( AAT=ATA=E )组成的集合是矩阵群(Matrix Group)的一种
    特殊正交群Special Orthogonal Group SOn 在正交群定义的基础上再补充定义,所有的矩阵行列式为1是矩阵群(Matrix Group)的一种
    通常 SOn 被称为n维旋转群(Rotation Group)
    它所指定的旋转对应的旋转轴可以通过求解一个线性方程组的基础解析来计算得到
    酉群Unitary Group Un 复数域上所有n级酉矩阵组成的集合,对于矩阵乘法所成的群
    特殊酉群Special Unitary Group SUn 在酉群定义的基础上再补充定义,所有的矩阵行列式为1
    集合 Ω 的全变换群Full Transformation Group on Set Ω SΩ 非空集合 Ω 到自身的所有双射组成的集合,对于映射的乘法构成的一个群
    n元对称群Symmetric Group on n letters Sn SΩ ,当 Ω 为有限集合时 Sn 具备对称性
    这时其中的每一个元素(是一个双射)被称为 Ω 的一个置换(permutation),对于 Ω 有n个元素的情形,该置换被称为n元置换(permutation on n letters)
    Sn 中引入了r-轮换(r-cycle)的概念;特别的,当r=2时,轮换被称为对换(transposition);并且可以说明:每一个置换都可以表示成一些对换的乘积
    并且对于置换进一步引入了由其等价的对换分解式中的对换的个数的奇偶性确定的奇置换或偶置换
    n元交错群Alternating Group on n letters An Sn 中所有偶置换组成的集合

    References

    展开全文
  • 在《Gin源码解析例子——路由》一文中,我们已经初识中间件。本文将继续探讨这个技术。(转载请指明出于breaksoftware的csdn博客)  Gin的中间件,本质是一个匿名回调函数。这和绑定到一个路径的处理函数本质...
  • android json解析及简单例子

    万次阅读 多人点赞 2011-11-23 11:10:14
    JSON的定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据交换...
  • 二维码生成原理及解析代码

    万次阅读 多人点赞 2017-12-18 22:35:06
    二维码生成原理及解析代码 自从大街小巷的小商小贩都开始布满了腾讯爸爸和阿里爸爸的二维码之后,我才感觉到我大天朝共享支付的优越性。最近毕业论文写的差不多了,在入职之前多学一些东西也是好的。这里秉着好奇心...
  •  (静态成员没有定义) 一个典型的例子是使用单例模式时忘记在类外给静态变量instance实例进行初始化。 A build dependency is only defined as a project dependency in the solution.(有项目依赖时不能仅建立...
  • 01 — 二项分布如果实验满足以下两种条件: 在每次试验中只有两种可能的结果,而且两种结果发生与否互相对立;...例子解析例如,一堆苹果有好的,有坏的,从中取10次,定义随机变量:从中取得好苹果的个数
  • C++隐藏的概念(例子解析

    千次阅读 热门讨论 2018-06-01 18:33:09
    在继承机制,派生类的类域被嵌套在基类的类域中。派生类的名字解析过程如下: 1、首先在派生类类域中查找该名字。 2、如果第一步中没有成功查找到该名字,即在派生类的类域中无法对该名字进行解析,则编译器在...
  • 关于解析延拓

    千次阅读 2018-10-06 23:16:35
    不严谨地来讲,解析延拓就是将解析函数(处处可微的复函数)的定义域进行 合理地“扩大” 。 运用此方法,本来在某些值函数是无意义的(比如级数不收敛),延拓后可以在此取到确定的值。 当然就像我说的,这种延拓...
  •  当我们对SpringMVC控制的资源发起请求时,这些请求都会被SpringMVC的DispatcherServlet处理,接着Spring会分析看哪一个HandlerMapping定义的所有请求映射中存在对该请求的最合理的映射。然后通过该HandlerMapping...
  • 各种python 函数参数定义解析

    万次阅读 2014-04-18 17:25:22
    函数参数在调用过程中是如何解析的。首先说一下在python 中的函数调用过程是分四种方式的,这里且先说五种,第五种已经在以前的文章中说过了。 1.参数枚举的函数定义: >>> def Fun(a,b,c): return (a,b,c) >>> ...
  • 使用BeautifulSoup4解析XML

    万次阅读 多人点赞 2018-11-22 10:04:59
    Beautiful Soup 是一个用来从HTML或XML文件中提取数据的Python库,它利用大家所喜欢的解析器提供了许多惯用方法用来对文档树进行导航、查找和修改。 帮助文档英文版:...
  • 全面理解Gradle - 定义Task

    万次阅读 多人点赞 2017-12-26 00:14:00
    之前我们讲述了Groovy的语法,还讲述了Gradle的执行时序,本篇文章讲述Task的定义。 Task可以理解为Gradle的执行单元,实在是太重要了。根据前面的分析,Gradle通过一个个task来完成具体的构建任务,下面我们来看...
  • Java自定义注解及解析

    千次阅读 2018-03-28 16:36:52
    定义注解 注解处理器运行时解析注解 编译时解析注解自动生成.class代码 总结 注解 注解为我们在代码中添加信息提供一种形式化的方法,使我们可以在源码、编译时、运行时非常方便的使用...
  • python-def函数的定义

    万次阅读 多人点赞 2021-01-21 10:43:57
    无参定义函数 1. 语法 def function_name(): expressions Python 使用def 开始函数定义,紧接着是函数名,括号内部为函数的参数,内部为函数的 具体功能实现代码,如果想要函数有返回值, 在 expressions 中的...
  • golang 自定义json解析

    千次阅读 2018-10-17 18:53:32
    在实际开发中,经常会遇到需要定制json编解码。...如果希望自己定义对象的编码和解码方式,需要实现以下两个接口: type Marshaler interface { MarshalJSON() ([]byte, error) } type Unmarshaler interfa...
  • 生活中有趣的概率论例子

    千次阅读 2020-12-24 11:48:33
    生活中有趣的概率论例子概率论是数学的一个分支,它研究随机现象的数量规律,概率论的应用几乎遍及所有的科学领域,例如天气预报、 地震预报、产品的抽样调查,在通讯工程中概率论可用以提高信号的抗干扰性、分辨率...
  • DBC文件解析

    万次阅读 多人点赞 2020-02-03 15:53:14
    [ ]内容表示为可选部分,可以省略(如例子中即把该部分省略了); 但关键字”BS_:”必须存在,省略则会出错。《DBC File Format Document》规范中明确提醒,必须保留BS_: 标签。 2.3、网络节点的定义 格式如下:...
  • SqlServer触发器的格式+小例子解析

    千次阅读 2018-04-21 16:31:41
    触发器基本语法格式:create trigger tri_name on table for insert/update/deleted asSQL code触发器基本语法解析:create trigger tri_name //(tri_name:自定义触发器的名称)on table ...
  • DBC文件解析,基于致远电子DBC_demo

    万次阅读 热门讨论 2018-10-19 14:49:50
    致远电子官网上有DBC文件解析的demo,不过是用C++做的,...致远电子网上还有一个can数据收发的demo,有C#的例子,那我想就参考这个例子用C#做个DBC解析的软件好了,反正公司的软件也是用C#做的。 其实LibDBCManager.d...
  • javascript函数的三种定义方式区别

    千次阅读 2017-03-14 22:46:47
    js有三种定义函数的方式: 1.function 语句形式 2.函数直接量形式 3.通过Function构造函数形式定义函数 <script type="text/javascript"> //3种函数定义方式,前两种常用 /** * 1,...
  • Python对html解析(BeautifulSoup)

    万次阅读 2020-11-05 16:57:54
    一般情况,我们用的比较多的是 lxml 解析器。 BeautifulSoup安装  BeautifulSoup3 目前已经停止更新,推荐在现在的项目中使用BeautifulSoup4,不过它已经被移植到bs4了。也就是说导入时我们需要 import bs4 。...
  • 常用 XML 解析技术

    千次阅读 2018-04-12 10:44:09
    现在的软件项目都不是独立的一个项目,都是多系统协调工作。这样的话就涉及到系统间的通讯,通讯就会跟报文传输...Java 是一个开源的语言,本场 Chat 将给大家介绍一下常用的 XML 解析框架特点。 主要内容: XML ...
  • 经过前几个章节的介绍,终于把与列表解析的前置内容介绍完了,本节老猿将列表解析、字典解析、集合解析进行统一的介绍。 前面章节老猿好几次说到了要介绍列表解析,但老猿认为涉及知识层面比较多,一推再推,给人的...
  • JSON 格式数据的定义及操作

    千次阅读 2018-10-24 11:34:48
    SON —— JavaScript Object Notation,JavaScript对象表示法。 JSON 是一种数据格式,而不是一种编程语言。虽然具有相同的语法形式...很多编程语言都有针对 JSON 的解析器和序列化器。 JSON 的语法可以表示以下三...
  • 带通滤波器和带阻滤波器详细解析:(定义,区别,工作原理,经典电路图,应用) [导读]带通滤波器:它允许一定频段的信号通过,抑制低于或高于该频段的信号、干扰和噪声;带阻滤波器:它抑制一定频段内的信号,...
  • verilog宏定义

    万次阅读 2019-03-16 09:43:41
    Verilog可以像这样定义宏 `define TESTEQ1 4’b1101 。 定义的宏可以用在后面的代码中,如 if (request == `TESTEQ1。使用`ifdef和`endif可以检测是否定义了某个宏,相当于条件编译。如果`ifedf调用的宏被定义过,...
  • wsdl文件解析

    千次阅读 2018-08-04 12:42:01
    在这个例子中,端口 "glossaryTerms" 定义了一个名为 "setTerm" 的 one-way 操作。 这个 "setTerm" 操作可接受新术语表项目消息的输入,这些消息使用一条名为 "newTermValues" 的消息,此消息带有输入参数 "term...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 417,499
精华内容 166,999
关键字:

下定义的例子及解析