From 9d42087149a6870965896be74dc6260f72d2cac9 Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Thu, 12 Oct 2017 13:06:35 +0200 Subject: VPPAPIGEN: vppapigen replacement in Python PLY. This is a version of the VPP API generator in Python PLY. It supports the existing language, and has a plugin architecture for generators. Currently C and JSON are supported. Changes: - vl_api_version to option version = "major.minor.patch" - enum support - Added error checking and reporting - import support (removed the C pre-processor) - services (tying request/reply together) Version: option version = "1.0.0"; Enum: enum colours { RED, BLUE = 50, }; define foo { vl_api_colours_t colours; }; Services: service { rpc foo returns foo_reply; rpc foo_dump returns stream foo_details; rpc want_stats returns want_stats_reply events ip4_counters, ip6_counters; }; Future planned features: - unions - bool, text - array support (including length) - proto3 output plugin - Refactor C/C++ generator as a plugin - Refactor Java generator as a plugin Change-Id: Ifa289966c790e1b1a8e2938a91e69331e3a58bdf Signed-off-by: Ole Troan --- src/tools/vppapigen/C.py | 279 ++++++ src/tools/vppapigen/JSON.py | 66 ++ src/tools/vppapigen/gram.y | 96 -- src/tools/vppapigen/lex.c | 1129 ----------------------- src/tools/vppapigen/lex.h | 51 -- src/tools/vppapigen/node.c | 1602 --------------------------------- src/tools/vppapigen/node.h | 102 --- src/tools/vppapigen/test_vppapigen.py | 80 ++ src/tools/vppapigen/vppapigen | 1 + src/tools/vppapigen/vppapigen.py | 745 +++++++++++++++ 10 files changed, 1171 insertions(+), 2980 deletions(-) create mode 100644 src/tools/vppapigen/C.py create mode 100644 src/tools/vppapigen/JSON.py delete mode 100644 src/tools/vppapigen/gram.y delete mode 100644 src/tools/vppapigen/lex.c delete mode 100644 src/tools/vppapigen/lex.h delete mode 100644 src/tools/vppapigen/node.c delete mode 100644 src/tools/vppapigen/node.h create mode 100755 src/tools/vppapigen/test_vppapigen.py create mode 120000 src/tools/vppapigen/vppapigen create mode 100755 src/tools/vppapigen/vppapigen.py (limited to 'src/tools/vppapigen') diff --git a/src/tools/vppapigen/C.py b/src/tools/vppapigen/C.py new file mode 100644 index 00000000000..2a8930666fa --- /dev/null +++ b/src/tools/vppapigen/C.py @@ -0,0 +1,279 @@ +# C generation +import datetime +import os + +datestring = datetime.datetime.now() +input_filename = 'inputfil' +top_boilerplate = '''\ +/* + * VLIB API definitions {datestring} + * Input file: {input_filename} + * Automatically generated: please edit the input file NOT this file! + */ + +#if defined(vl_msg_id)||defined(vl_union_id) \\ + || defined(vl_printfun) ||defined(vl_endianfun) \\ + || defined(vl_api_version)||defined(vl_typedefs) \\ + || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\ + || defined(vl_api_version_tuple) +/* ok, something was selected */ +#else +#warning no content included from {input_filename} +#endif + +#define VL_API_PACKED(x) x __attribute__ ((packed)) +''' + +bottom_boilerplate = '''\ +/****** API CRC (whole file) *****/ + +#ifdef vl_api_version +vl_api_version({input_filename}, {file_crc:#08x}) + +#endif +''' + + +def msg_ids(s): + output = '''\ + +/****** Message ID / handler enum ******/ + +#ifdef vl_msg_id +''' + + for t in s['defines']: + output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \ + (t.name.upper(), t.name) + output += "#endif" + + return output + + +def msg_names(s): + output = '''\ + +/****** Message names ******/ + +#ifdef vl_msg_name +''' + + for t in s['defines']: + dont_trace = 0 if t.dont_trace else 1 + output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace) + output += "#endif" + + return output + + +def msg_name_crc_list(s, suffix): + output = '''\ + +/****** Message name, crc list ******/ + +#ifdef vl_msg_name_crc_list +''' + output += "#define foreach_vl_msg_name_crc_%s " % suffix + + for t in s['defines']: + output += "\\\n_(VL_API_%s, %s, %08x) " % \ + (t.name.upper(), t.name, t.crc) + output += "\n#endif" + + return output + + +def duplicate_wrapper_head(name): + s = "#ifndef defined_%s\n" % name + s += "#define defined_%s\n" % name + return s + + +def duplicate_wrapper_tail(): + return '#endif\n\n' + + +def typedefs(s, filename): + name = filename.replace('.', '_') + output = '''\ + + +/****** Typedefs ******/ + +#ifdef vl_typedefs +#ifndef included_{module} +#define included_{module} +''' + output = output.format(module=name) + for e in s['enums']: + output += duplicate_wrapper_head(e.name) + output += "typedef enum {\n" + for b in e.block: + output += " %s = %s,\n" % (b[0], b[1]) + output += '} vl_api_%s_t;\n' % e.name + output += duplicate_wrapper_tail() + + for t in s['typedefs'] + s['defines']: + output += duplicate_wrapper_head(t.name) + output += "typedef VL_API_PACKED(struct _vl_api_%s {\n" % t.name + for b in t.block: + if b.type == 'Field': + output += " %s %s;\n" % (b.fieldtype, b.fieldname) + elif b.type == 'Array': + if b.lengthfield: + output += " %s %s[0];\n" % (b.fieldtype, b.fieldname) + else: + output += " %s %s[%s];\n" % (b.fieldtype, b.fieldname, + b.length) + else: + raise ValueError("Error in processing array type %s" % b) + + output += '}) vl_api_%s_t;\n' % t.name + output += duplicate_wrapper_tail() + + output += "\n#endif" + output += "\n#endif\n\n" + + return output + + +format_strings = {'u8': '%u', + 'i8': '%d', + 'u16': '%u', + 'i16': '%d', + 'u32': '%u', + 'i32': '%ld', + 'u64': '%llu', + 'i64': '%llu', + 'f64': '%.2f', } + + +def printfun(s): + output = '''\ +/****** Print functions *****/ +#ifdef vl_printfun + +#ifdef LP64 +#define _uword_fmt \"%lld\" +#define _uword_cast (long long) +#else +#define _uword_fmt \"%ld\" +#define _uword_cast long +#endif + +''' + for t in s['typedefs'] + s['defines']: + if t.manual_print: + output += "/***** manual: vl_api_%s_t_print *****/\n\n" % t.name + continue + output += duplicate_wrapper_head(t.name + '_t_print') + output += "static inline void *vl_api_%s_t_print (vl_api_%s_t *a," % \ + (t.name, t.name) + output += "void *handle)\n{\n" + output += " vl_print(handle, \"vl_api_%s_t:\\n\");\n" % t.name + + for o in t.block: + if o.type != 'Field': + continue + if o.fieldtype in format_strings: + output += " vl_print(handle, \"%s: %s\\n\", a->%s);\n" % \ + (o.fieldname, format_strings[o.fieldtype], + o.fieldname) + + output += ' return handle;\n' + output += '}\n\n' + output += duplicate_wrapper_tail() + + output += "\n#endif /* vl_printfun */\n" + + return output + + +endian_strings = { + 'u16': 'clib_net_to_host_u16', + 'u32': 'clib_net_to_host_u32', + 'u64': 'clib_net_to_host_u64', + 'i16': 'clib_net_to_host_u16', + 'i32': 'clib_net_to_host_u32', + 'i64': 'clib_net_to_host_u64', +} + + +def endianfun(s): + output = '''\ + +/****** Endian swap functions *****/\n\ +#ifdef vl_endianfun + +#undef clib_net_to_host_uword +#ifdef LP64 +#define clib_net_to_host_uword clib_net_to_host_u64 +#else +#define clib_net_to_host_uword clib_net_to_host_u32 +#endif + +''' + + for t in s['typedefs'] + s['defines']: + if t.manual_endian: + output += "/***** manual: vl_api_%s_t_endian *****/\n\n" % t.name + continue + output += duplicate_wrapper_head(t.name + '_t_endian') + output += "static inline void vl_api_%s_t_endian (vl_api_%s_t *a)" % \ + (t.name, t.name) + output += "\n{\n" + + for o in t.block: + if o.type != 'Field': + continue + if o.fieldtype in endian_strings: + output += " a->%s = %s(a->%s);\n" % \ + (o.fieldname, endian_strings[o.fieldtype], o.fieldname) + else: + output += " /* a->%s = a->%s (no-op) */\n" % \ + (o.fieldname, o.fieldname) + + output += '}\n\n' + output += duplicate_wrapper_tail() + output += "\n#endif /* vl_endianfun */\n\n" + + return output + + +def version_tuple(s, module): + output = '''\ +/****** Version tuple *****/ + +#ifdef vl_api_version_tuple + +''' + if 'version' in s['options']: + v = s['options']['version'] + (major, minor, patch) = v.split('.') + output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \ + (module, major, minor, patch) + + output += "\n#endif /* vl_api_version_tuple */\n\n" + + return output + + +# +# Plugin entry point +# +def run(input_filename, s, file_crc): + basename = os.path.basename(input_filename) + filename, file_extension = os.path.splitext(basename) + output = top_boilerplate.format(datestring=datestring, + input_filename=basename) + output += msg_ids(s) + output += msg_names(s) + output += msg_name_crc_list(s, filename) + output += typedefs(s, filename + file_extension) + output += printfun(s) + output += endianfun(s) + output += version_tuple(s, basename) + output += bottom_boilerplate.format(input_filename=basename, + file_crc=file_crc) + + return output diff --git a/src/tools/vppapigen/JSON.py b/src/tools/vppapigen/JSON.py new file mode 100644 index 00000000000..d1f47ebc560 --- /dev/null +++ b/src/tools/vppapigen/JSON.py @@ -0,0 +1,66 @@ +# JSON generation +import json + + +def walk_enums(s): + r = [] + for e in s: + d = [] + d.append(e.name) + for b in e.block: + d.append(b) + d.append({'enumtype': e.enumtype}) + r.append(d) + return r + + +def walk_services(s): + r = [] + for e in s: + d = {'reply': e.reply} + if e.stream: + d['stream'] = True + if e.events: + d['events'] = e.events + r.append({e.caller: d}) + return r + + +def walk_defs(s): + r = [] + for t in s: + d = [] + d.append(t.name) + for b in t.block: + f = [] + if b.type == 'Field': + f = [b.fieldtype, b.fieldname] + elif b.type == 'Array': + if b.lengthfield: + f = [b.fieldtype, b.fieldname, b.length, b.lengthfield] + else: + f = [b.fieldtype, b.fieldname, b.length] + else: + raise ValueError("Error in processing array type %s" % b) + d.append(f) + if t.crc: + c = {} + c['crc'] = "{0:#0{1}x}".format(t.crc, 10) + d.append(c) + + r.append(d) + return r + + +# +# Plugin entry point +# +def run(filename, s, file_crc): + j = {} + + j['types'] = walk_defs(s['typedefs']) + j['messages'] = walk_defs(s['defines']) + j['enums'] = walk_enums(s['enums']) + j['services'] = walk_services(s['services']) + j['vl_api_version'] = hex(file_crc) + return json.dumps(j, indent=4, separators=(',', ': ')) diff --git a/src/tools/vppapigen/gram.y b/src/tools/vppapigen/gram.y deleted file mode 100644 index 5c5d46ff4e0..00000000000 --- a/src/tools/vppapigen/gram.y +++ /dev/null @@ -1,96 +0,0 @@ -%{ -/* - * gram.y - message definition language - * - * Copyright (c) 2009 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -extern void yyerror (char *s); -extern int yylex (void); - -#define YYSTYPE void * - -void generate (YYSTYPE); - YYSTYPE add_slist(YYSTYPE, YYSTYPE); - YYSTYPE add_define(YYSTYPE, YYSTYPE); - YYSTYPE suppress_version(void); - YYSTYPE add_defbody(YYSTYPE, YYSTYPE); - YYSTYPE add_primtype(YYSTYPE, YYSTYPE, YYSTYPE); - YYSTYPE add_complex(YYSTYPE, YYSTYPE); - YYSTYPE add_union(YYSTYPE, YYSTYPE); - YYSTYPE add_scalar_vbl(YYSTYPE); - YYSTYPE add_vector_vbl(YYSTYPE, YYSTYPE); - YYSTYPE add_variable_length_vector_vbl(YYSTYPE, YYSTYPE); - YYSTYPE set_flags(YYSTYPE, YYSTYPE); - YYSTYPE add_version(YYSTYPE, YYSTYPE, YYSTYPE); -%} - -%token NAME RPAR LPAR SEMI LBRACK RBRACK NUMBER PRIMTYPE BARF -%token TPACKED DEFINE LCURLY RCURLY STRING UNION -%token HELPER_STRING COMMA DOT VL_API_VERSION -%token NOVERSION MANUAL_PRINT MANUAL_ENDIAN TYPEONLY DONT_TRACE AUTOREPLY - -%% - -pgm: slist {generate ($1);} - ; - -slist: slist stmt {$$ = add_slist ($1, $2);} - | stmt {$$ = $1;} - ; - -stmt: flist defn {$$ = set_flags($1, $2);} - | defn {$$ = $1;} - | api_version {$$ = $1;} - ; - -flist: flist flag {$$ = (YYSTYPE)(unsigned long) - ((unsigned long) $1 - | (unsigned long) $2);} - | flag {$$ = $1;} - ; - -flag: - MANUAL_PRINT {$$ = $1;} - | MANUAL_ENDIAN {$$ = $1;} - | DONT_TRACE {$$ = $1;} - | TYPEONLY {$$ = $1;} - | AUTOREPLY {$$ = $1;} - ; - -defn: DEFINE NAME LCURLY defbody RCURLY SEMI - {$$ = add_define($2, $4);} - - | NOVERSION SEMI - {$$ = suppress_version();} - ; - -defbody: defbody onedef {$$ = add_defbody($1, $2);} - | onedef {$$ = $1;} - ; - -onedef: PRIMTYPE vbl SEMI {$$ = add_primtype($1, $2, 0);} - | TPACKED PRIMTYPE vbl SEMI {$$ = add_primtype($1, $2, $3);} - | NAME vbl SEMI {$$ = add_complex($1, $2);} - | UNION NAME LCURLY defbody RCURLY SEMI - {$$ = add_union($2, $4);} - ; - -vbl: NAME {$$ = add_scalar_vbl($1);} - | NAME LBRACK NUMBER RBRACK {$$ = add_vector_vbl($1, $3);} - | NAME LBRACK NAME RBRACK {$$ = add_variable_length_vector_vbl($1, $3);} - ; - -api_version: VL_API_VERSION NUMBER DOT NUMBER DOT NUMBER - {$$ = add_version ($2, $4, $6);} diff --git a/src/tools/vppapigen/lex.c b/src/tools/vppapigen/lex.c deleted file mode 100644 index d9f82c2b9f1..00000000000 --- a/src/tools/vppapigen/lex.c +++ /dev/null @@ -1,1129 +0,0 @@ -/* - *------------------------------------------------------------------ - * lex.c - API generator lexical analyzer - * - * Copyright (c) 1996-2009 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ - -#include -#include -#include -#include -#include -#include - -#include "lex.h" -#include "node.h" -#include "tools/vppapigen/gram.h" -#include -#include -#include - -FILE *ifp, *ofp, *pythonfp, *jsonfp; -char *vlib_app_name = "vpp"; -int dump_tree; -time_t starttime; -char *input_filename; -char *current_filename; -int current_filename_allocated; -unsigned long input_crc; -unsigned long message_crc; -int yydebug; -char *push_input_fifo; -char saved_ungetc_char; -char have_ungetc_char; - -/* - * lexer variable definitions - */ - -static const char *version = "0.1"; -static int the_lexer_linenumber = 1; -static enum lex_state the_lexer_state = START_STATE; - -/* - * private prototypes - */ -static void usage (char *); -static int name_check (const char *, YYSTYPE *); -static int name_compare (const char *, const char *); -extern int yydebug; -extern YYSTYPE yylval; - -unsigned int crc32c_table[256] = { - 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, - 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, - 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, - 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, - 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, - 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, - 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, - 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, - 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, - 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, - 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, - 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, - 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, - 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, - 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, - 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, - 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, - 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, - 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, - 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, - 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, - 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, - 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, - 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, - 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, - 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, - 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, - 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, - 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, - 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, - 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, - 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, - 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, - 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, - 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, - 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, - 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, - 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, - 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, - 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, - 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, - 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, - 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, - 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, - 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, - 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, - 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, - 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, - 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, - 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, - 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, - 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, - 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, - 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, - 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, - 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, - 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, - 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, - 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, - 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, - 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, - 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, - 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, - 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 -}; - -static inline unsigned long CRC8 (unsigned long crc, - unsigned char d) -{ - return ((crc >> 8) ^ crc32c_table[(crc ^ d) & 0xFF]); -} -static inline unsigned long CRC16 (unsigned long crc, - unsigned short d) -{ - crc = CRC8 (crc, d & 0xff); - d = d >> 8; - crc = CRC8 (crc, d & 0xff); - return crc; -} - - -static unsigned long -crc_eliding_c_comments (const char *buf, unsigned long crc) -{ - const char *p; - enum { cOTHER, /* */ - cSTRING, /* "... */ - cSBACKSLASH, /* "...\ */ - cCHAR, /* '... */ - cCBACKSLASH, /* '...\ */ - cSLASH, /* / */ - cSLASH_SLASH, /* //... */ - cSLASH_STAR, /* / *... */ - cSTAR /* / *...* */ - } ss = cOTHER; - - for (p = buf; ;) { - unsigned char c = *p++; - - switch (c) { - case 0: - switch (ss) { - case cOTHER: - return (crc); - case cSTRING: case cSBACKSLASH: - case cCHAR: case cCBACKSLASH: - case cSLASH: case cSLASH_SLASH: case cSLASH_STAR: case cSTAR: - fprintf (stderr, "Inopportune EOF: %s\n", buf); - exit (1); - } - break; - case '\"': - switch (ss) { - case cOTHER: ss = cSTRING; break; /* start string */ - case cSTRING: ss = cOTHER; break; /* end string */ - case cSBACKSLASH: ss = cSTRING; break; - case cCHAR: break; - case cCBACKSLASH: ss = cCHAR; break; - case cSLASH: crc = CRC8 (crc, '/'); ss = cOTHER; break; - case cSLASH_SLASH: continue; /* in comment */ - case cSLASH_STAR: continue; /* in comment */ - case cSTAR: ss = cSLASH_STAR; continue; /* in comment */ - } - break; - case '\\': - switch (ss) { - case cOTHER: break; - case cSTRING: ss = cSBACKSLASH; break; - case cSBACKSLASH: ss = cSTRING; break; - case cCHAR: ss = cCBACKSLASH; break; - case cCBACKSLASH: ss = cCHAR; break; - case cSLASH: crc = CRC8 (crc, '/'); ; ss = cOTHER; break; - case cSLASH_SLASH: continue; /* in comment */ - case cSLASH_STAR: continue; /* in comment */ - case cSTAR: ss = cSLASH_STAR; continue; /* in comment */ - } - break; - case '/': - switch (ss) { - case cOTHER: ss = cSLASH; continue; /* potential comment */ - case cSTRING: break; - case cSBACKSLASH: ss = cSTRING; break; - case cCHAR: break; - case cCBACKSLASH: ss = cCHAR; break; - case cSLASH: ss = cSLASH_SLASH; continue; /* start comment */ - case cSLASH_SLASH: continue; /* in comment */ - case cSLASH_STAR: continue; /* in comment */ - case cSTAR: ss = cOTHER; continue; /* end of comment */ - } - break; - case '*': - switch (ss) { - case cOTHER: break; - case cSTRING: break; - case cSBACKSLASH: ss = cSTRING; break; - case cCHAR: break; - case cCBACKSLASH: ss = cCHAR; break; - case cSLASH: ss = cSLASH_STAR; continue; /* start comment */ - case cSLASH_SLASH: continue; /* in comment */ - case cSLASH_STAR: ss = cSTAR; continue; /* potential end */ - case cSTAR: continue; /* still potential end of comment */ - } - break; - case '\n': case '\r': case ' ': case '\t': case '\014': - switch (ss) { - case cOTHER: continue; /* ignore all whitespace */ - case cSTRING: break; - case cSBACKSLASH: ss = cSTRING; break; - case cCHAR: break; - case cCBACKSLASH: ss = cCHAR; break; - case cSLASH: c = '/'; ss = cOTHER; break; - case cSLASH_SLASH: - if (c == '\n' || c == '\r') ss = cOTHER; /* end comment */ - continue; - case cSLASH_STAR: continue; /* in comment */ - case cSTAR: ss = cSLASH_STAR; continue; /* in comment */ - } - default: - switch (ss) { - case cOTHER: break; - case cSTRING: break; - case cSBACKSLASH: ss = cSTRING; break; - case cCHAR: break; - case cCBACKSLASH: ss = cCHAR; break; - case cSLASH: crc = CRC8 (crc, '/'); ss = cOTHER; break; - case cSLASH_SLASH: continue; /* in comment */ - case cSLASH_STAR: continue; /* in comment */ - case cSTAR: ss = cSLASH_STAR; continue; /* in comment */ - } - } - crc = CRC8 (crc, c); - } -} - -/* - * main - */ -int main (int argc, char **argv) -{ - int curarg = 1; - char *ofile=0; - char *pythonfile=0; - char *jsonfile=0; - char *show_name=0; - - while (curarg < argc) { - if (!strncmp (argv [curarg], "--verbose", 3)) { - fprintf (stderr, "%s version %s\n", argv [0], version); - curarg++; - continue; - } - - if (!strncmp (argv [curarg], "--yydebug", 3)) { - yydebug = 1; - curarg++; - continue; - } - - if (!strncmp (argv [curarg], "--dump", 3)) { - dump_tree = 1; - curarg++; - continue; - } - - if (!strncmp (argv[curarg], "--show-name", 3)) { - curarg++; - if (curarg < argc) { - show_name = argv[curarg]; - curarg++; - continue; - } else { - fprintf(stderr, "Missing filename after --show-name \n"); - exit(1); - } - } - - if (!strncmp (argv [curarg], "--input", 3)) { - curarg++; - if (curarg < argc) { - input_filename = argv[curarg]; - if (!strcmp (argv [curarg], "-")) - ifp = stdin; - else - ifp = fopen (argv [curarg], "r"); - if (ifp == NULL) { - fprintf (stderr, "Couldn't open input file %s\n", - argv[curarg]); - exit (1); - } - curarg++; - } else { - fprintf(stderr, "Missing filename after --input\n"); - exit(1); - } - continue; - } - if (!strncmp (argv [curarg], "--output", 3)) { - curarg++; - if (curarg < argc) { - ofp = fopen (argv[curarg], "w"); - if (ofp == NULL) { - fprintf (stderr, "Couldn't open output file %s\n", - argv[curarg]); - exit (1); - } - ofile = argv[curarg]; - curarg++; - } else { - fprintf(stderr, "Missing filename after --output\n"); - exit(1); - } - continue; - } - if (!strncmp (argv [curarg], "--python", 8)) { - curarg++; - if (curarg < argc) { - if (!strcmp(argv[curarg], "-")) { - pythonfp = stdout; - } else { - pythonfp = fopen(argv[curarg], "w"); - pythonfile = argv[curarg]; - } - if (pythonfp == NULL) { - fprintf (stderr, "Couldn't open python output file %s\n", - argv[curarg]); - exit (1); - } - curarg++; - } else { - fprintf(stderr, "Missing filename after --python\n"); - exit(1); - } - continue; - } - if (!strncmp (argv [curarg], "--json", 6)) { - curarg++; - if (curarg < argc) { - if (!strcmp(argv[curarg], "-")) { - jsonfp = stdout; - } else { - jsonfp = fopen(argv[curarg], "w"); - jsonfile = argv[curarg]; - } - if (jsonfp == NULL) { - fprintf (stderr, "Couldn't open JSON output file %s\n", - argv[curarg]); - exit (1); - } - curarg++; - } else { - fprintf(stderr, "Missing filename after --json\n"); - exit(1); - } - continue; - } - if (!strncmp (argv [curarg], "--app", 4)) { - curarg++; - if (curarg < argc) { - vlib_app_name = argv[curarg]; - curarg++; - } else { - fprintf(stderr, "Missing app name after --app\n"); - exit(1); - } - continue; - } - - usage(argv[0]); - exit (1); - } - if (ofp == NULL) { - ofile = 0; - } - if (pythonfp == NULL) { - pythonfile = 0; - } - if (jsonfp == NULL) { - jsonfile = 0; - } - if (ifp == NULL) { - fprintf(stderr, "No input file specified...\n"); - exit(1); - } - if (show_name) { - input_filename = show_name; - } - - starttime = time (0); - - if (yyparse() == 0) { - fclose (ifp); - curarg -= 2; - if (ofile) { - printf ("Output written to %s\n", ofile); - fclose (ofp); - } - if (pythonfile) { - printf ("Python bindings written to %s\n", pythonfile); - fclose (pythonfp); - } - if (jsonfile) { - printf ("JSON bindings written to %s\n", jsonfile); - fclose (jsonfp); - } - } - else { - fclose (ifp); - if (ofp) - fclose (ofp); - if (ofile) { - printf ("Removing %s\n", ofile); - unlink (ofile); - } - if (pythonfile) { - printf ("Removing %s\n", pythonfile); - unlink (pythonfile); - } - if (jsonfile) { - printf ("Removing %s\n", jsonfile); - unlink (jsonfile); - } - exit (1); - } - exit (0); -} - -/* - * usage - */ -static void usage (char *progname) -{ - fprintf (stderr, - "usage: %s --input [--output ] " - "[--json ] [--python ]\n%s", - progname, - " [--yydebug] [--dump-tree]\n"); - exit (1); -} - -/* - * yyerror - */ -void yyerror (char *s) -{ - fprintf (stderr, "%s:%d %s\n", current_filename, the_lexer_linenumber, s); -} - -static char namebuf [MAXNAME]; - -static inline char -getc_char (FILE *ifp) -{ - char rv; - - if (have_ungetc_char) { - have_ungetc_char = 0; - return saved_ungetc_char; - } - - if (clib_fifo_elts (push_input_fifo)) { - clib_fifo_sub1(push_input_fifo, rv); - return (rv & 0x7f); - } - return ((char)(getc(ifp) & 0x7f)); -} - -u32 fe (char *fifo) -{ - return clib_fifo_elts (fifo); -} - -static inline void -ungetc_char (char c, FILE *ifp) -{ - saved_ungetc_char = c; - have_ungetc_char = 1; -} - -void autoreply (void *np_arg) -{ - static u8 *s; - node_t *np = (node_t *)np_arg; - int i; - - vec_reset_length (s); - - s = format (0, " define %s_reply\n", (char *)(np->data[0])); - s = format (s, "{\n"); - s = format (s, " u32 context;\n"); - s = format (s, " i32 retval;\n"); - s = format (s, "};\n"); - - for (i = 0; i < vec_len (s); i++) - clib_fifo_add1 (push_input_fifo, s[i]); -} - -/* - * yylex (well, yylex_1: The real yylex below does crc-hackery) - */ -static int yylex_1 (void) -{ - int nameidx=0; - char c; - enum { LP_INITIAL_WHITESPACE, LP_LINE_NUMBER, - LP_PRE_FILENAME_WHITESPACE, LP_FILENAME, - LP_POST_FILENAME, - LP_OTHER - } lp_substate = LP_INITIAL_WHITESPACE; - - again: - switch (the_lexer_state) { - /* - * START state -- looking for something interesting - */ - case START_STATE: - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - - switch (c) { - case '\n': - the_lexer_linenumber++; - goto again; - - case '#': - the_lexer_state = LINE_PRAGMA_STATE; - lp_substate = LP_INITIAL_WHITESPACE; - goto again; - - /* FALLTHROUGH */ - case '\t': - case ' ': - goto again; - - case '(': - return (LPAR); - - case ')': - return (RPAR); - - case ';': - return (SEMI); - - case '[': - return (LBRACK); - - case ']': - return (RBRACK); - - case '{': - return (LCURLY); - - case '}': - return (RCURLY); - - case ',': - return (COMMA); - - case '.': - return (DOT); - - case '"': - nameidx = 0; - the_lexer_state = STRING_STATE; - goto again; - - case '@': - nameidx = 0; - the_lexer_state = HELPER_STATE; - goto again; - - case '/': - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - - if (c == '/') { - the_lexer_state = CPP_COMMENT_STATE; - goto again; - } else if (c == '*') { - the_lexer_state = C_COMMENT_STATE; - goto again; - } else { - fprintf (stderr, "unknown token /%c at line %d\n", - c, the_lexer_linenumber); - return (BARF); - } - - case '\\': - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - - /* Note fallthrough... */ - - default: - if (isalpha (c) || c == '_') { - namebuf [0] = c; - nameidx = 1; - the_lexer_state = NAME_STATE; - goto again; - } else if (isdigit(c)) { - namebuf [0] = c; - nameidx = 1; - the_lexer_state = NUMBER_STATE; - goto again; - } - - fprintf (stderr, "unknown token %c at line %d\n", - c, the_lexer_linenumber); - return (BARF); - } - - /* - * NAME state -- eat the rest of a name - */ - case NAME_STATE: - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - - if (!isalnum (c) && c != '_') { - ungetc_char (c, ifp); - namebuf [nameidx] = 0; - the_lexer_state = START_STATE; - return (name_check (namebuf, &yylval)); - } - if (nameidx >= (MAXNAME-1)) { - fprintf(stderr, "lex input buffer overflow...\n"); - exit(1); - } - namebuf [nameidx++] = c; - goto again; - - /* - * NUMBER state -- eat the rest of a number - */ - case NUMBER_STATE: - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - - if (!isdigit (c)) { - ungetc_char (c, ifp); - namebuf [nameidx] = 0; - the_lexer_state = START_STATE; - yylval = (void *) atol(namebuf); - return (NUMBER); - } - if (nameidx >= (MAXNAME-1)) { - fprintf(stderr, "lex input buffer overflow...\n"); - exit(1); - } - namebuf [nameidx++] = c; - goto again; - - /* - * C_COMMENT state -- eat a peach - */ - case C_COMMENT_STATE: - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - if (c == '*') { - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - if (c == '/') { - the_lexer_state = START_STATE; - goto again; - } - } - if (c == '\n') - the_lexer_linenumber++; - goto again; - - /* - * CPP_COMMENT state -- eat a plum - */ - - case CPP_COMMENT_STATE: - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - if (c == '\n') { - the_lexer_linenumber++; - the_lexer_state = START_STATE; - goto again; - } - goto again; - - case STRING_STATE: - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - switch (c) { - case '\\': - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - namebuf[nameidx++] = c; - goto again; - - case '"': - namebuf[nameidx] = 0; - yylval = (YYSTYPE) sxerox (namebuf); - the_lexer_state = START_STATE; - return (STRING); - - default: - if (c == '\n') - the_lexer_linenumber++; - - if (nameidx >= (MAXNAME-1)) { - fprintf(stderr, "lex input buffer overflow...\n"); - exit(1); - } - namebuf[nameidx++] = c; - goto again; - } - break; - - case HELPER_STATE: - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - switch (c) { - case '\\': - c = getc_char (ifp); - if (feof (ifp)) - return (EOF); - namebuf[nameidx] = c; - goto again; - - case '@': - namebuf[nameidx] = 0; - yylval = (YYSTYPE) sxerox (namebuf); - the_lexer_state = START_STATE; - return (HELPER_STRING); - - default: - if (c == '\n') - the_lexer_linenumber++; - - /* - * CPP makes it approximately impossible to - * type "#define FOO 123", so we provide a - * lexical trick to achieve that result - */ - - if (c == '$') - c = '#'; - - if (nameidx >= (MAXNAME-1)) { - fprintf(stderr, "lex input buffer overflow...\n"); - exit(1); - } - namebuf[nameidx++] = c; - goto again; - } - break; - - case LINE_PRAGMA_STATE: - /* We're only interested in lines of the form # 259 "foo.c" 17 */ - - switch (lp_substate) { - - case LP_INITIAL_WHITESPACE: /* no number seen yet */ - c = getc_char(ifp); - if (feof(ifp)) - return(EOF); - if (c >= '0' && c <= '9') { - namebuf[nameidx++] = c; - lp_substate = LP_LINE_NUMBER; - } else if (c == '\n') { - goto lp_end_of_line; - } else if (c != ' ' && c != '\t') { - /* Nothing */ - } else { - lp_substate = LP_OTHER; - } - goto again; - - case LP_LINE_NUMBER: /* eating linenumber */ - c = getc_char(ifp); - if (feof(ifp)) - return(EOF); - if (c >= '0' && c <= '9') { - namebuf[nameidx++] = c; - } else if (c == ' ' || c == '\t') { - namebuf[nameidx++] = 0; - the_lexer_linenumber = atol(namebuf); - lp_substate = LP_PRE_FILENAME_WHITESPACE; - } else if (c == '\n') { - goto lp_end_of_line; - } else { - lp_substate = LP_OTHER; - } - goto again; - - case LP_PRE_FILENAME_WHITESPACE: /* awaiting filename */ - c = getc_char(ifp); - if (feof(ifp)) - return(EOF); - - if (c == '"') { - lp_substate = LP_FILENAME; - nameidx = 0; - } else if (c == ' ' || c == '\t') { - /* nothing */ - } else if (c == '\n') { - goto lp_end_of_line; - } else { - lp_substate = LP_OTHER; - } - goto again; - - case LP_FILENAME: /* eating filename */ - c = getc_char(ifp); - if (feof(ifp)) - return(EOF); - - if (c == '"') { - lp_substate = LP_POST_FILENAME; - namebuf[nameidx] = 0; - } else if (c == '\n') { - goto lp_end_of_line; /* syntax error... */ - } else { - namebuf[nameidx++] = c; - } - goto again; - - case LP_POST_FILENAME: /* ignoring rest of line */ - case LP_OTHER: - c = getc_char(ifp); - if (feof(ifp)) - return(EOF); - - if (c == '\n') { - if (lp_substate == LP_POST_FILENAME) { - if (current_filename_allocated) { - current_filename_allocated = 0; - free(current_filename); - } - - if (!strcmp(namebuf, "")) { - current_filename = input_filename; - } else { - current_filename = sxerox(namebuf); - current_filename_allocated = 1; - } - } - lp_end_of_line: - the_lexer_state = START_STATE; - nameidx = 0; - } - goto again; - } - break; - } - fprintf (stderr, "LEXER BUG!\n"); - exit (1); - /* NOTREACHED */ - return (0); -} - -/* - * Parse a token and side-effect input_crc - * in a whitespace- and comment-insensitive fashion. - */ -int yylex (void) -{ - /* - * Accumulate a crc32-based signature while processing the - * input file. The goal is to come up with a magic number - * which changes precisely when the original input file changes - * but which ignores whitespace changes. - */ - unsigned long crc = input_crc; - int node_type = yylex_1 (); - unsigned long crc2 = message_crc; - int use_helper_string = 0; - unsigned short code; - - switch (node_type) { - case PRIMTYPE: - case NAME: - case NUMBER: - case STRING: - case HELPER_STRING: - use_helper_string = 1; - break; - - /* Other node types have no "substate" */ - /* This code is written in this curious fashion because we - * want the generated CRC to be independent of the particular - * values a particular version of lex/bison assigned to various states. - */ - - case RPAR: code = 258; break; - case LPAR: code = 259; break; - case SEMI: code = 260; break; - case LBRACK: code = 261; break; - case RBRACK: code = 262; break; - case BARF: code = 265; break; - case TPACKED: code = 266; break; - case DEFINE: code = 267; break; - case LCURLY: code = 268; break; - case RCURLY: code = 269; break; - case UNION: code = 271; break; - case COMMA: code = 273; break; - case NOVERSION: code = 274; break; - case MANUAL_PRINT: code = 275; break; - case MANUAL_ENDIAN: code = 276; break; - case TYPEONLY: code = 278; break; - case DONT_TRACE: code = 279; break; - case AUTOREPLY: code = 280; break; - case DOT: code = 281; break; - case VL_API_VERSION: code = 282; break; - - case EOF: code = ~0; break; /* hysterical compatibility */ - - default: - fprintf(stderr, "yylex: node_type %d missing state CRC cookie\n", - node_type); - exit(1); - } - - if (use_helper_string) - { - /* We know these types accumulated token text into namebuf */ - /* HELPER_STRING may still contain C comments. Argh. */ - crc = crc_eliding_c_comments (namebuf, crc); - crc2 = crc_eliding_c_comments (namebuf, crc2); - } else - { - crc = CRC16 (crc, code); - crc2 = CRC16 (crc2, code); - } - - input_crc = crc; - message_crc = crc2; - return (node_type); -} - -/* - * name_check -- see if the name we just ate - * matches a known keyword. If so, set yylval - * to a new instance of , and return PARSER_MACRO - * - * Otherwise, set yylval to sxerox (s) and return NAME - */ - -static struct keytab { - char *name; - enum node_subclass subclass_id; -} keytab [] = -/* Keep the table sorted, binary search used below! */ -{ - {"autoreply", NODE_AUTOREPLY}, - {"define", NODE_DEFINE}, - {"dont_trace", NODE_DONT_TRACE}, - {"f64", NODE_F64}, - {"i16", NODE_I16}, - {"i32", NODE_I32}, - {"i64", NODE_I64}, - {"i8", NODE_I8}, - {"manual_endian", NODE_MANUAL_ENDIAN}, - {"manual_print", NODE_MANUAL_PRINT}, - {"noversion", NODE_NOVERSION}, - {"packed", NODE_PACKED}, - {"typeonly", NODE_TYPEONLY}, - {"u16", NODE_U16}, - {"u32", NODE_U32}, - {"u64", NODE_U64}, - {"u8", NODE_U8}, - {"union", NODE_UNION}, - {"uword", NODE_UWORD}, - {"vl_api_version", NODE_VERSION}, -}; - -static int name_check (const char *s, YYSTYPE *token_value) -{ - enum node_subclass subclass_id; - int top, bot, mid; - int result; - - for (top = 0, bot = (sizeof(keytab) / sizeof(struct keytab))-1; - bot >= top; ) { - mid = (top + bot) / 2; - result = name_compare (s, keytab[mid].name); - if (result < 0) - bot = mid - 1; - else if (result > 0) - top = mid + 1; - else { - subclass_id = keytab[mid].subclass_id; - - switch (subclass_id) { - case NODE_U8: - case NODE_U16: - case NODE_U32: - case NODE_U64: - case NODE_I8: - case NODE_I16: - case NODE_I32: - case NODE_I64: - case NODE_F64: - case NODE_UWORD: - *token_value = make_node(subclass_id); - return (PRIMTYPE); - - case NODE_PACKED: - *token_value = make_node(subclass_id); - return (TPACKED); - - case NODE_DEFINE: - message_crc = 0; - *token_value = make_node(subclass_id); - return(DEFINE); - - case NODE_MANUAL_PRINT: - *token_value = (YYSTYPE) NODE_FLAG_MANUAL_PRINT; - return (MANUAL_PRINT); - - case NODE_MANUAL_ENDIAN: - *token_value = (YYSTYPE) NODE_FLAG_MANUAL_ENDIAN; - return (MANUAL_ENDIAN); - - case NODE_TYPEONLY: - *token_value = (YYSTYPE) NODE_FLAG_TYPEONLY; - return(TYPEONLY); - - case NODE_DONT_TRACE: - *token_value = (YYSTYPE) NODE_FLAG_DONT_TRACE; - return(DONT_TRACE); - - case NODE_AUTOREPLY: - *token_value = (YYSTYPE) NODE_FLAG_AUTOREPLY; - return(AUTOREPLY); - - case NODE_NOVERSION: - return(NOVERSION); - - case NODE_VERSION: - return(VL_API_VERSION); - - case NODE_UNION: - return(UNION); - - default: - fprintf (stderr, "fatal: keytab botch!\n"); - exit (1); - } - } - } - *token_value = (YYSTYPE) sxerox (s); - return (NAME); -} - -/* - * sxerox - */ - -char *sxerox (const char *s) -{ - int len = strlen (s); - char *rv; - - rv = (char *) malloc (len+1); - if (rv == 0) { - fprintf(stderr, "Out of memory..."); - exit (1); - } - - strcpy (rv, s); - return (rv); -} - -/* - * name_compare - */ - -int name_compare (const char *s1, const char *s2) -{ - char c1, c2; - - while (*s1 && *s2) { - c1 = *s1++; - c2 = *s2++; - - c1 = tolower (c1); - c2 = tolower (c2); - if (c1 < c2) - return (-1); - else if (c1 > c2) - return (1); - } - if (*s1 < *s2) - return (-1); - else if (*s1 > *s2) - return (1); - return (0); -} diff --git a/src/tools/vppapigen/lex.h b/src/tools/vppapigen/lex.h deleted file mode 100644 index 275cf685a4b..00000000000 --- a/src/tools/vppapigen/lex.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - *------------------------------------------------------------------ - * lex.h - definitions for the api generator's lexical - * analyzer. - * - * Copyright (c) 1996-2009 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ - -#ifndef _LEX_H_ -#define _LEX_H_ 1 - -extern int yylex (void); -extern void yyerror (char *); -extern int yyparse (void); -extern void autoreply (void *); - -#ifndef YYSTYPE -#define YYSTYPE void * -#endif - -#include "tools/vppapigen/gram.h" - -enum lex_state { - START_STATE = 1, - NAME_STATE, - NUMBER_STATE, - C_COMMENT_STATE, - CPP_COMMENT_STATE, - STRING_STATE, - HELPER_STATE, - LINE_PRAGMA_STATE, -}; - -#define MAXNAME 64000 - -extern unsigned long input_crc; -extern unsigned long message_crc; - -#endif /* _LEX_H_ */ diff --git a/src/tools/vppapigen/node.c b/src/tools/vppapigen/node.c deleted file mode 100644 index 37ba4931a1f..00000000000 --- a/src/tools/vppapigen/node.c +++ /dev/null @@ -1,1602 +0,0 @@ -/* - *------------------------------------------------------------------ - * node.c - the api generator's semantic back-end - * - * Copyright (c) 2004-2009 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lex.h" -#include "node.h" - -#define YYSTYPE void * - -FILE *ofp; -FILE *pythonfp; -FILE *jsonfp; -time_t starttime; -char *vlib_app_name; -char *input_filename; -node_vft_t *the_vft[NODE_N_TYPES]; -static u32 indent; -static int dont_output_version; -int dump_tree; -static char *fixed_name; -static char tmpbuf [MAXNAME]; -static char *current_def_name; -static char *current_union_name; -static char *current_type_fmt; -static char *current_type_cast; -static char current_id; -static char current_is_complex; -static char *current_endianfun; -static char *current_type_name; - -void indent_me(FILE *ofp) -{ - int i; - - for (i = 0; i < indent; i++) - putc(' ', ofp); -} - -char *uppercase (char *s) -{ - char *cp; - - cp = tmpbuf; - - while (*s && (cp < tmpbuf + (sizeof(tmpbuf)-1))) { - if (*s >= 'a' && *s <= 'z') - *cp++ = *s++ - ('a' - 'A'); - else - *cp++ = *s++; - } - *cp = 0; - return(tmpbuf); -} - -char *lowercase (char *s) -{ - char *cp; - - cp = tmpbuf; - - while (*s && (cp < tmpbuf + (sizeof(tmpbuf)-1))) { - if (*s >= 'A' && *s <= 'Z') - *cp++ = *s++ + ('a' - 'A'); - else - *cp++ = *s++; - } - *cp = 0; - return(tmpbuf); -} - -void primtype_recursive_print(node_t *this, i8 *fmt) -{ - fputs((char *)fmt, stdout); - - if (this->deeper) { - node_vft_t *vftp = the_vft[this->deeper->type]; - vftp->print(this->deeper); - } -} - -void primtype_recursive_generate(node_t *this, enum passid which, FILE *ofp, - i8 *type_name, i8 *type_fmt, i8 *type_cast) -{ - node_vft_t *vftp; - - current_type_name = (char *)type_name; - current_type_cast = (char *)type_cast; - - switch(which) { - case TYPEDEF_PASS: - fputs((char *)type_name, ofp); - fputs(" ", ofp); - break; - - case PRINTFUN_PASS: - current_type_fmt = (char *)type_fmt; - break; - - case ENDIANFUN_PASS: - vftp = the_vft[this->type]; - current_endianfun = vftp->endian_converter; - break; - - case PYTHON_PASS: - fputs("('", pythonfp); - fputs((char *)type_name, pythonfp); - fputs("', ", pythonfp); - break; - - case JSON_PASS: - fputs("[\"", jsonfp); - fputs((char *)type_name, jsonfp); - fputs("\", ", jsonfp); - break; - - default: - fprintf(stderr, "primtype_recursive_generate: unimp pass %d\n", which); - break; - } - - if (this->deeper) { - vftp = the_vft[this->deeper->type]; - vftp->generate(this->deeper, which, ofp); - } -} - -void node_illegal_print (node_t *this) -{ - fprintf(stderr, "node_illegal_print called\n"); - exit(0); -} - -void node_illegal_generate (node_t *this, enum passid notused, FILE *ofp) -{ - fprintf(stderr, "node_illegal_generate called\n"); - exit(0); -} - -node_vft_t node_illegal_vft = { - node_illegal_print, - node_illegal_generate, - "illegal" -}; - -void node_u8_print (node_t *this) -{ - primtype_recursive_print(this, "u8 "); -} - -void node_u8_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "u8", "%u", "(unsigned)"); -} - -node_vft_t node_u8_vft = { - node_u8_print, - node_u8_generate, - NULL -}; - -void node_u16_print (node_t *this) -{ - primtype_recursive_print(this, "u16 "); -} - -void node_u16_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "u16", "%u", "(unsigned)"); -} - -node_vft_t node_u16_vft = { - node_u16_print, - node_u16_generate, - "clib_net_to_host_u16" -}; - -void node_u32_print (node_t *this) -{ - primtype_recursive_print(this, "u32 "); -} - -void node_u32_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "u32", "%u", "(unsigned)"); -} - -node_vft_t node_u32_vft = { - node_u32_print, - node_u32_generate, - "clib_net_to_host_u32", -}; - -void node_u64_print (node_t *this) -{ - primtype_recursive_print(this, "u64 "); -} - -void node_u64_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "u64", "%llu", - "(long long)"); -} - -node_vft_t node_u64_vft = { - node_u64_print, - node_u64_generate, - "clib_net_to_host_u64" -}; - -void node_i8_print (node_t *this) -{ - primtype_recursive_print(this, "i8 "); -} - -void node_i8_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "i8", "%d", "(int)"); -} - -node_vft_t node_i8_vft = { - node_i8_print, - node_i8_generate, - "" -}; - -void node_i16_print (node_t *this) -{ - primtype_recursive_print(this, "i16 "); -} - -void node_i16_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "i16", "%d", "(int)"); -} - -node_vft_t node_i16_vft = { - node_i16_print, - node_i16_generate, - "clib_net_to_host_u16" -}; - -void node_i32_print (node_t *this) -{ - primtype_recursive_print(this, "i32 "); -} - -void node_i32_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "i32", "%ld", "(long)"); -} - -node_vft_t node_i32_vft = { - node_i32_print, - node_i32_generate, - "clib_net_to_host_u32" -}; - -void node_i64_print (node_t *this) -{ - primtype_recursive_print(this, "i64 "); -} - -void node_i64_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "i64", "%lld", - "(long long)"); -} - -node_vft_t node_i64_vft = { - node_i64_print, - node_i64_generate, - "clib_net_to_host_u64" -}; - -void node_f64_print (node_t *this) -{ - primtype_recursive_print(this, "f64 "); -} - -void node_f64_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "f64", "%.2f", - "(double)"); -} - -node_vft_t node_f64_vft = { - node_f64_print, - node_f64_generate, - " ", /* FP numbers are sent in host byte order */ -}; - - -void node_packed_print (node_t *this) -{ - primtype_recursive_print (this, "packed "); -} - -void node_packed_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "PACKED", "", ""); -} - -node_vft_t node_packed_vft = { - node_packed_print, - node_packed_generate, - 0, -}; - -void node_define_print (node_t *this) -{ - fprintf(stdout, "define %s {\n", CDATA0); - if (this->deeper) { - node_vft_t *vftp = the_vft[this->deeper->type]; - fprintf(stdout, " "); - vftp->print(this->deeper); - } - fprintf(stdout, "};\n"); -} - -void node_define_generate (node_t *this, enum passid which, FILE *fp) -{ - node_t *child; - - switch(which) { - case TYPEDEF_PASS: - fprintf(fp, "typedef VL_API_PACKED(struct _vl_api_%s {\n", CDATA0); - child = this->deeper; - indent += 4; - while (child) { - node_vft_t *vftp = the_vft[child->type]; - indent_me(fp); - vftp->generate(child, which, fp); - child = child->peer; - } - indent -= 4; - fprintf(fp, "}) vl_api_%s_t;\n\n", CDATA0); - break; - - case ENDIANFUN_PASS: - case PRINTFUN_PASS: - child = this->deeper; - while (child) { - node_vft_t *vftp = the_vft[child->type]; - vftp->generate(child, which, fp); - child = child->peer; - } - break; - - case PYTHON_PASS: - fprintf(fp, "('%s',\n", CDATA0); - child = this->deeper; - indent += 4; - while (child) { - node_vft_t *vftp = the_vft[child->type]; - indent_me(fp); - vftp->generate(child, which, fp); - child = child->peer; - } - indent -= 4; - fprintf(fp, "),\n\n"); - break; - - case JSON_PASS: - fprintf(fp, "[\"%s\",\n", CDATA0); - child = this->deeper; - indent += 4; - while (child) { - node_vft_t *vftp = the_vft[child->type]; - indent_me(fp); - vftp->generate(child, which, fp); - child = child->peer; - fprintf(fp, ",\n"); - } - indent_me(fp); - fprintf (fp, "{\"crc\" : \"0x%08x\"}\n", (u32)(uword)CDATA3); - indent -= 4; - indent_me(fp); - fprintf(fp, "]"); - break; - - default: - fprintf(stderr, "node_define_generate: unimp pass %d\n", which); - break; - } -} - -node_vft_t node_define_vft = { - node_define_print, - node_define_generate, - 0, -}; - -void node_union_print (node_t *this) -{ - primtype_recursive_print (this, "union "); -} - -void node_union_generate (node_t *this, enum passid which, FILE *fp) -{ - node_t *child; - node_t *uelem; - int case_id=1; - - switch(which) { - case TYPEDEF_PASS: - fprintf(fp, "u8 _%s_which;\n", CDATA0); - indent_me(fp); - fprintf(fp, "union _%s {\n", CDATA0); - child = this->deeper; - indent += 4; - - while (child) { - node_vft_t *vftp = the_vft[child->type]; - indent_me(fp); - vftp->generate(child, which, fp); - child = child->peer; - } - indent -= 4; - indent_me(fp); - fprintf(fp, "} %s;\n", CDATA0); - break; - - case PRINTFUN_PASS: - case ENDIANFUN_PASS: - uelem = this->deeper; - - indent_me(fp); - fprintf(fp, "switch(a->_%s_which) {\n", - CDATA0); - indent += 4; - current_union_name = CDATA0; - - /* Walk the list of objects in this union */ - while (uelem) { - node_vft_t *vftp = the_vft[uelem->type]; - indent -= 4; - indent_me(fp); - fprintf(fp, "case %d:\n", case_id); - case_id++; - indent += 4; - /* Drill down on each element */ - vftp->generate(uelem, which, fp); - indent_me(fp); - fprintf(fp, "break;\n"); - uelem = uelem->peer; - } - current_union_name = 0; - indent -= 4; - indent_me(fp); - fprintf(fp, "default:\n"); - indent += 4; - indent_me(fp); - if (which == PRINTFUN_PASS) { - fprintf(fp, - "vl_print(handle, \"WARNING: _%s_which not set.\\n\");\n", - CDATA0); - } - indent_me(fp); - fprintf(fp, "break;\n"); - indent -= 4; - indent_me(fp); - fprintf(fp, "}\n"); - break; - - default: - fprintf(stderr, "node_union_generate: unimp pass %d\n", which); - break; - } -} - - -node_vft_t node_union_vft = { - node_union_print, - node_union_generate, - 0, -}; - -void node_scalar_print (node_t *this) -{ - fprintf(stdout, "%s", CDATA0); - primtype_recursive_print (this, ""); -} - -void node_scalar_generate (node_t *this, enum passid which, FILE *fp) -{ - char *union_prefix = ""; - - if (current_union_name) { - sprintf(tmpbuf, "%s.", current_union_name); - union_prefix = tmpbuf; - } - - switch(which) { - case TYPEDEF_PASS: - fprintf(fp, "%s;\n", CDATA0); - break; - - case PRINTFUN_PASS: - indent_me(fp); - if (current_is_complex) { - fprintf(fp, "vl_api_%s_t_print(a->%s%s, handle);\n", - current_type_name, union_prefix, CDATA0); - } else { - if (!strcmp(current_type_fmt, "uword")) { - fprintf(fp, - "vl_print(handle, \"%s%s: \" _uword_fmt \"\\n\", %s a->%s%s);\n", - union_prefix, CDATA0, "(_uword_cast)", - union_prefix, CDATA0); - } else { - fprintf(fp, - "vl_print(handle, \"%s%s: %s\\n\", %s a->%s%s);\n", - union_prefix, CDATA0, - current_type_fmt, current_type_cast, - union_prefix, CDATA0); - } - } - break; - - case ENDIANFUN_PASS: - indent_me(fp); - if (current_is_complex) { - fprintf(fp, "vl_api%s_t_endian(a->%s%s);\n", - current_type_name, union_prefix, CDATA0); - } else { - /* Current_endianfun == NULL means e.g. it's a u8... */ - if (current_endianfun) { - fprintf(fp, "a->%s%s = %s(a->%s%s);\n", union_prefix, - CDATA0, current_endianfun, - union_prefix, CDATA0); - } else { - fprintf(fp, "/* a->%s%s = a->%s%s (no-op) */\n", - union_prefix, CDATA0, - union_prefix, CDATA0); - } - } - break; - case PYTHON_PASS: - fprintf(fp, "'%s'),\n", CDATA0); - break; - - case JSON_PASS: - fprintf(fp, "\"%s\"]", CDATA0); - break; - - default: - fprintf(stderr, "node_scalar_generate: unimp pass %d\n", which); - } - if (this->deeper) { - fprintf(stderr, "broken recursion in node_scalar_generate\n"); - } -} - - -node_vft_t node_scalar_vft = { - node_scalar_print, - node_scalar_generate, - 0, -}; - -void node_vector_print (node_t *this) -{ - primtype_recursive_print (this, "vector "); -} - -void node_vector_generate (node_t *this, enum passid which, FILE *fp) -{ - char *union_prefix = ""; - - if (current_union_name) { - sprintf(tmpbuf, "%s.", current_union_name); - union_prefix = tmpbuf; - } - - switch(which) { - case TYPEDEF_PASS: - fprintf(fp, "%s[%d];\n", CDATA0, IDATA1); - break; - - case PRINTFUN_PASS: - /* Don't bother about "u8 data [0];" et al. */ - if (IDATA1 == 0) - break; - - indent_me(fp); - fprintf(fp, "{\n"); - indent += 4; - indent_me(fp); - fprintf(fp, "int _i;\n"); - indent_me(fp); - fprintf(fp, "for (_i = 0; _i < %d; _i++) {\n", - IDATA1); - indent += 4; - indent_me(fp); - if (current_is_complex) { - fprintf(fp, "vl_print(handle, \"%s%s[%%d]: ", - union_prefix, CDATA0); - fprintf(fp, - "vl_print_%s (handle, a->%s%s[_i]);\n", - CDATA0, union_prefix, CDATA0); - } else { - fprintf(fp, - "vl_print(handle, \"%s%s[%%d]: %s\\n\", _i, a->%s%s[_i]);\n", - union_prefix, CDATA0, - current_type_fmt, - union_prefix, CDATA0); - } - indent -= 4; - indent_me(fp); - fprintf(fp, "}\n"); - indent -= 4; - indent_me(fp); - fprintf(fp, "}\n"); - break; - - case ENDIANFUN_PASS: - /* Don't bother about "u8 data [0];" et al. */ - if (IDATA1 == 0) - break; - /* If this is a simple endian swap, but the endian swap method is a no-op, - * then indicate this is a no-op in a comment. - */ - if (!current_is_complex && current_endianfun == NULL) { - indent_me(fp); - fprintf(fp, "/* a->%s%s[0..%d] = a->%s%s[0..%d] (no-op) */\n", - union_prefix, CDATA0, IDATA1 - 1, - union_prefix, CDATA0, IDATA1 - 1); - break; - } - - indent_me(fp); - fprintf(fp, "{\n"); - indent += 4; - indent_me(fp); - fprintf(fp, "int _i;\n"); - indent_me(fp); - fprintf(fp, "for (_i = 0; _i < %d; _i++) {\n", - IDATA1); - indent += 4; - indent_me(fp); - if (current_is_complex) { - fprintf(fp, - "vl_api_%s_t_endian (a->%s%s[_i]);\n", - current_type_name, union_prefix, CDATA0); - } else { - fprintf(fp, - "a->%s%s[_i] = %s(a->%s%s[_i]);\n", - union_prefix, CDATA0, - current_endianfun, - union_prefix, CDATA0); - } - indent -= 4; - indent_me(fp); - fprintf(fp, "}\n"); - indent -= 4; - indent_me(fp); - fprintf(fp, "}\n"); - break; - case PYTHON_PASS: - if (CDATA2 != 0) { // variable length vector - fprintf(fp, "'%s', '%d', '%s'),\n", CDATA0, IDATA1, CDATA2); - } else { - fprintf(fp, "'%s', '%d'),\n", CDATA0, IDATA1); - } - break; - - case JSON_PASS: - if (CDATA2 != 0) { /* variable length vector */ - fprintf(fp, "\"%s\", %d, \"%s\"]", CDATA0, IDATA1, CDATA2); - } else { - fprintf(fp, "\"%s\", %d]", CDATA0, IDATA1); - } - break; - - default: - fprintf(stderr, "node_vector_generate: unimp pass %d\n", which); - } - if (this->deeper) { - fprintf(stderr, "broken recursion in node_vector_generate\n"); - } -} - -node_vft_t node_vector_vft = { - node_vector_print, - node_vector_generate, - 0, -}; - -void node_complex_print (node_t *this) -{ - primtype_recursive_print (this, "complex "); -} - -void node_complex_generate (node_t *this, enum passid which, FILE *fp) -{ - node_t *deeper; - node_vft_t *vftp; - char *member_name = "broken!"; - char *union_prefix = ""; - - if (current_union_name) { - sprintf(tmpbuf, "%s.", current_union_name); - union_prefix = tmpbuf; - } - - current_is_complex++; - - switch(which) { - case TYPEDEF_PASS: - fprintf(fp, "%s ", CDATA0); - deeper = this->deeper; - if (deeper) { - vftp = the_vft[deeper->type]; - vftp->generate(deeper, which, fp); - } - break; - - case PRINTFUN_PASS: - deeper = this->deeper; - while (deeper) { - if (deeper->type == NODE_SCALAR || - deeper->type == NODE_VECTOR) { - member_name = deeper->data[0]; - break; - } - deeper = deeper->deeper; - } - indent_me(fp); - fprintf(fp, "vl_print(handle, \"%s%s ----- \\n\");\n", - union_prefix, member_name); - indent_me(fp); - - if (deeper && deeper->type == NODE_VECTOR) - fprintf(fp, "%s_print(a->%s%s, handle);\n", - CDATA0, union_prefix, member_name); - else - fprintf(fp, "%s_print(&a->%s%s, handle);\n", - CDATA0, union_prefix, member_name); - - indent_me(fp); - fprintf(fp, "vl_print(handle, \"%s%s ----- END \\n\");\n", - union_prefix, member_name); - break; - - case ENDIANFUN_PASS: - deeper = this->deeper; - while (deeper) { - if (deeper->type == NODE_SCALAR || - deeper->type == NODE_VECTOR) { - member_name = deeper->data[0]; - break; - } - deeper = deeper->deeper; - } - - indent_me(fp); - if (deeper && deeper->type == NODE_VECTOR) - fprintf(fp, "%s_endian(a->%s%s);\n", - CDATA0, union_prefix, member_name); - else - fprintf(fp, "%s_endian(&a->%s%s);\n", - CDATA0, union_prefix, member_name); - break; - case PYTHON_PASS: - fprintf(fp, "('%s',", CDATA0); - deeper = this->deeper; - if (deeper) { - vftp = the_vft[deeper->type]; - vftp->generate(deeper, which, fp); - } - break; - - case JSON_PASS: - fprintf(fp, "[\"%s\", ", CDATA0); - deeper = this->deeper; - if (deeper) { - vftp = the_vft[deeper->type]; - vftp->generate(deeper, which, fp); - } - break; - - default: - fprintf(stderr, "node_complex_generate unimp pass %d...\n", which); - break; - } - current_is_complex--; -} - -node_vft_t node_complex_vft = { - node_complex_print, - node_complex_generate, - 0, -}; - -void node_noversion_print (node_t *this) -{ - primtype_recursive_print (this, "noversion "); -} - -void node_noversion_generate (node_t *this, enum passid which, FILE *ofp) -{ - fprintf(stderr, "node_noversion_generate called...\n"); -} - -node_vft_t node_noversion_vft = { - node_noversion_print, - node_noversion_generate, - 0, -}; - -void node_version_print (node_t *this) -{ - primtype_recursive_print (this, "version "); -} - -void node_version_generate (node_t *this, enum passid which, FILE *ofp) -{ - fprintf(stderr, "node_version_generate called...\n"); -} - -node_vft_t node_version_vft = { - node_version_print, - node_version_generate, - 0, -}; - -void node_uword_print (node_t *this) -{ - primtype_recursive_print(this, "uword "); -} - -void node_uword_generate (node_t *this, enum passid which, FILE *ofp) -{ - primtype_recursive_generate(this, which, ofp, "uword", "uword", ""); -} - -node_vft_t node_uword_vft = { - node_uword_print, - node_uword_generate, - "clib_net_to_host_uword", -}; - -node_vft_t *the_vft[NODE_N_TYPES] = { - &node_illegal_vft, - &node_u8_vft, - &node_u16_vft, - &node_u32_vft, - &node_u64_vft, - &node_i8_vft, - &node_i16_vft, - &node_i32_vft, - &node_i64_vft, - &node_f64_vft, - &node_packed_vft, - &node_define_vft, - &node_union_vft, - &node_scalar_vft, - &node_vector_vft, - &node_complex_vft, - &node_noversion_vft, - &node_version_vft, - &node_uword_vft, -}; - -void *make_node (enum node_subclass type) -{ - node_t *rv; - - rv = (node_t *) malloc (sizeof (*rv)); - if (rv == 0) { - fprintf (stderr, "fatal: make_node out of memory\n"); - exit (1); - } - bzero (rv, sizeof (*rv)); - rv->type = type; - return ((void *) rv); -} - -YYSTYPE deeper (YYSTYPE arg1, YYSTYPE arg2) -{ - node_t *np1 = (node_t *) arg1; - node_t *np2 = (node_t *) arg2; - node_t *hook_point; - - hook_point = np1; - - while (hook_point->deeper) - hook_point = hook_point->deeper; - - hook_point->deeper = np2; - return (arg1); -} - -YYSTYPE addpeer (YYSTYPE arg1, YYSTYPE arg2) -{ - node_t *np1 = (node_t *) arg1; - node_t *np2 = (node_t *) arg2; - node_t *hook_point; - - hook_point = np1; - - while (hook_point->peer) - hook_point = hook_point->peer; - - hook_point->peer = np2; - return (arg1); -} - -/* - * add_slist (stmt_list, stmt) - */ - -YYSTYPE add_slist (YYSTYPE a1, YYSTYPE a2) -{ - if (a1 && a2) - return (addpeer(a1, a2)); - else if(a1) - return(a1); - else - return(a2); -} - -/* - * add_define (char *name, defn_list); - */ -YYSTYPE add_define (YYSTYPE a1, YYSTYPE a2) -{ - node_t *np; - - np = make_node(NODE_DEFINE); - np->data[0] = a1; - np->data[3] = (void *) message_crc; - deeper((YYSTYPE)np, a2); - return ((YYSTYPE) np); -} - -/* - * add_defbody (defn_list, new_defn) - */ -YYSTYPE add_defbody (YYSTYPE a1, YYSTYPE a2) -{ - return (addpeer(a1, a2)); -} - -/* - * add_primtype ([packed], primitive type, instance) - */ - -YYSTYPE add_primtype (YYSTYPE a1, YYSTYPE a2, YYSTYPE a3) -{ - /* Hook instance to type node */ - deeper (a1, a2); - if (a3) { - deeper(a1, a3); - } - return (a1); -} - -/* - * add_complex(char *type_name, instance) - */ - -YYSTYPE add_complex (YYSTYPE a1, YYSTYPE a2) -{ - node_t *np; - - np = make_node(NODE_COMPLEX); - np->data[0] = (void *) a1; - - deeper((YYSTYPE)np, a2); - return ((YYSTYPE) np); -} - -/* - * add_union(char *type_name, definition) - */ - -YYSTYPE add_union (YYSTYPE a1, YYSTYPE a2) -{ - node_t *np; - - np = make_node(NODE_UNION); - np->data[0] = (void *) a1; - - deeper((YYSTYPE)np, a2); - return ((YYSTYPE) np); -} - - -/* - * add_vector_vbl (node_t *variable, YYSTYPE size) - */ - -YYSTYPE add_vector_vbl (YYSTYPE a1, YYSTYPE a2) -{ - node_t *np; - - np = make_node(NODE_VECTOR); - np->data[0] = (void *) a1; - np->data[1] = (void *) a2; - return ((YYSTYPE) np); -} - -/* - * add_vector_vbl (char *vector_name, char *vector_length_var) - */ - -YYSTYPE add_variable_length_vector_vbl (YYSTYPE vector_name, YYSTYPE vector_length_var) -{ - node_t *np; - - np = make_node(NODE_VECTOR); - np->data[0] = (void *) vector_name; - np->data[1] = (void *) 0; // vector size used for vpe.api.h generation (array of length zero) - np->data[2] = (void *) vector_length_var; // name of the variable that stores vector length - return ((YYSTYPE) np); -} - -/* - * add_scalar_vbl (char *name) - */ -YYSTYPE add_scalar_vbl (YYSTYPE a1) -{ - node_t *np; - - np = make_node(NODE_SCALAR); - np->data[0] = (void *) a1; - return ((YYSTYPE) np); -} - -/* - * set_flags (int flags, msg(=0?)) - */ -YYSTYPE set_flags(YYSTYPE a1, YYSTYPE a2) -{ - node_t *np; - int flags; - - np = (node_t *)a2; - if (!np) - return(0); - - flags = (int)(uword) a1; - - np->flags |= flags; - - /* Generate a foo_reply_t right here */ - if (flags & NODE_FLAG_AUTOREPLY) - autoreply(np); - - return (a2); -} -/* - * suppress_version - */ -YYSTYPE suppress_version (void) -{ - dont_output_version = 1; - return (0); -} - -void dump(node_t *np) -{ - node_vft_t *vftp; - - while (np) { - vftp = the_vft[np->type]; - vftp->print(np); - np = np->peer; - } -} - -char *fixup_input_filename(void) -{ - char *cp; - - cp = (char *)input_filename; - - while (*cp) - cp++; - - cp--; - - while (cp > input_filename && *cp != '/') - cp--; - if (*cp == '/') - cp++; - - strncpy (tmpbuf, cp, sizeof(tmpbuf)-1); - - cp = tmpbuf; - - while (*cp) - cp++; - - cp--; - - while (cp > tmpbuf && *cp != '.') - cp--; - - if (*cp == '.') - *cp = 0; - - return (sxerox(tmpbuf)); -} - -void generate_top_boilerplate(FILE *fp) - -{ - time_t curtime; - char *datestring; - char *source_date_epoch; - if ((source_date_epoch = getenv("SOURCE_DATE_EPOCH")) == NULL || (curtime = (time_t)strtol(source_date_epoch, NULL, 10)) <= 0) - curtime = starttime; - datestring = asctime(gmtime(&curtime)); - fixed_name = fixup_input_filename(); - - datestring[24] = 0; - - fprintf (fp, "/*\n"); - fprintf (fp, " * VLIB API definitions %s\n", datestring); - fprintf (fp, " * Input file: %s\n", input_filename); - fprintf (fp, " * Automatically generated: please edit the input file "); - fprintf (fp, "NOT this file!\n"); - fprintf (fp, " */\n\n"); - fprintf (fp, "#if defined(vl_msg_id)||defined(vl_union_id)||"); - fprintf (fp, "defined(vl_printfun) \\\n ||defined(vl_endianfun)||"); - fprintf (fp, " defined(vl_api_version)||defined(vl_typedefs) \\\n"); - fprintf (fp, " ||defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\\n"); - fprintf (fp, " ||defined(vl_api_version_tuple)\n"); - fprintf (fp, "/* ok, something was selected */\n"); - fprintf (fp, "#else\n"); - fprintf (fp, "#warning no content included from %s\n", input_filename); - fprintf (fp, "#endif\n\n"); - fprintf (fp, "#define VL_API_PACKED(x) x __attribute__ ((packed))\n\n"); -} - -void generate_bottom_boilerplate(FILE *fp) - -{ - fprintf(fp, "/****** API CRC (whole file) *****/\n\n"); - fprintf (fp, "#ifdef vl_api_version\n"); - - if (dont_output_version) { - fprintf (fp, "/* WARNING: API FILE VERSION CHECK DISABLED */\n"); - input_crc = 0; - } - - fprintf (fp, "vl_api_version(%s, 0x%08x)\n\n", - fixed_name, (unsigned int)input_crc); - - fprintf (fp, "#endif\n\n"); -} - -void generate_msg_ids(YYSTYPE a1, FILE *fp) -{ - node_t *np = (node_t *)a1; - - fprintf (fp, "\n/****** Message ID / handler enum ******/\n\n"); - fprintf (fp, "#ifdef vl_msg_id\n"); - - while (np) { - if (np->type == NODE_DEFINE) { - if (!(np->flags & NODE_FLAG_TYPEONLY)) { - fprintf (fp, "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n", - uppercase(np->data[0]), (i8 *)np->data[0]); - } else { - fprintf (fp, "/* typeonly: %s */\n", (i8 *)np->data[0]); - } - } - np = np->peer; - } - fprintf (fp, "#endif\n"); - -} - -void generate_msg_names(YYSTYPE a1, FILE *fp) -{ - node_t *np = (node_t *)a1; - - fprintf (fp, "\n/****** Message names ******/\n\n"); - - fprintf (fp, "#ifdef vl_msg_name\n"); - - while (np) { - if (np->type == NODE_DEFINE) { - if (!(np->flags & NODE_FLAG_TYPEONLY)) { - fprintf (fp, "vl_msg_name(vl_api_%s_t, %d)\n", - (i8 *) np->data[0], - (np->flags & NODE_FLAG_DONT_TRACE ? 0 : 1)); - } else { - fprintf (fp, "/* typeonly: %s */\n", (i8 *)np->data[0]); - } - } - np = np->peer; - } - fprintf (fp, "#endif\n\n"); -} - -void generate_msg_name_crc_list (YYSTYPE a1, FILE *fp) -{ - node_t *np = (node_t *)a1; - char *unique_suffix, *cp; - - unique_suffix = sxerox(fixed_name); - - cp = unique_suffix; - while (*cp && (*cp != '.')) - cp++; - if (*cp == '.') - *cp = 0; - - fprintf (fp, "\n/****** Message name, crc list ******/\n\n"); - - fprintf (fp, "#ifdef vl_msg_name_crc_list\n"); - fprintf (fp, "#define foreach_vl_msg_name_crc_%s ", unique_suffix); - - while (np) { - if (np->type == NODE_DEFINE) { - if (!(np->flags & NODE_FLAG_TYPEONLY)) { - fprintf (fp, "\\\n_(VL_API_%s, %s, %08x) ", - uppercase (np->data[0]), (i8 *) np->data[0], - (u32)(uword)np->data[3]); - } - } - np = np->peer; - } - fprintf (fp, "\n#endif\n\n"); - free (unique_suffix); -} - -void generate_typedefs(YYSTYPE a1, FILE *fp) -{ - node_t *np = (node_t *)a1; - node_vft_t *vftp; - - fprintf(fp, "\n/****** Typedefs *****/\n\n"); - fprintf(fp, "#ifdef vl_typedefs\n\n"); - - /* Walk the top-level node-list */ - while (np) { - if (np->type == NODE_DEFINE) { - /* Yeah, this is pedantic */ - vftp = the_vft[np->type]; - vftp->generate(np, TYPEDEF_PASS, fp); - } - np = np->peer; - } - fprintf(fp, "#endif /* vl_typedefs */\n\n"); -} - -void union_walk_one_defn(node_t *np, FILE *fp) -{ - node_t *vblp; - node_t *uelem; - - /* Walk the list of typed objects in this msg def */ - while (np) { - if (np->type == NODE_UNION) { - current_union_name = np->data[0]; - uelem = np->deeper; - - /* Walk the list of objects in this union */ - while (uelem) { - vblp = uelem->deeper; - /* Drill down on each element, find the variable name */ - while(vblp) { - if (vblp->type == NODE_SCALAR || - vblp->type == NODE_VECTOR || - vblp->type == NODE_COMPLEX) { - fprintf(ofp, "#define %s_", - uppercase(current_def_name)); - fprintf(ofp, "%s_", uppercase(current_union_name)); - fprintf(ofp, "%s %d\n",uppercase(vblp->data[0]), - current_id); - current_id++; - break; - } - vblp = vblp->deeper; - } - uelem = uelem->peer; - } - current_union_name = 0; - current_id = 1; - } - np = np->peer; - } -} - -void generate_uniondefs(YYSTYPE a1, FILE *fp) -{ - node_t *np = (node_t *)a1; - - fprintf(fp, "/****** Discriminated Union Definitions *****/\n\n"); - fprintf(fp, "#ifdef vl_union_id\n\n"); - - /* Walk the top-level node-list */ - while (np) { - if (np->type == NODE_DEFINE) { - current_id = 1; - current_def_name = np->data[0]; - union_walk_one_defn(np->deeper, fp); - } - np = np->peer; - } - fprintf(fp, "\n#endif /* vl_union_id */\n\n"); -} - -void generate_printfun(YYSTYPE a1, FILE *fp) -{ - node_t *np = (node_t *)a1; - node_vft_t *vftp; - - fprintf(fp, "/****** Print functions *****/\n\n"); - fprintf(fp, "#ifdef vl_printfun\n\n"); - - fprintf(fp, "#ifdef LP64\n"); - fputs ("#define _uword_fmt \"%lld\"\n", fp); - fputs ("#define _uword_cast (long long)\n", fp); - fprintf(fp, "#else\n"); - fputs("#define _uword_fmt \"%ld\"\n", fp); - fputs ("#define _uword_cast long\n", fp); - fprintf(fp, "#endif\n\n"); - - /* Walk the top-level node-list */ - while (np) { - if (np->type == NODE_DEFINE) { - if (!(np->flags & NODE_FLAG_MANUAL_PRINT)) { - fprintf(fp, - "static inline void *vl_api_%s_t_print (vl_api_%s_t *a,", - (i8 *)np->data[0], (i8 *) np->data[0]); - fprintf(fp, "void *handle)\n{\n"); - /* output the message name */ - fprintf(fp, - " vl_print(handle, \"vl_api_%s_t:\\n\");\n", - (i8 *)np->data[0]); - - indent += 4; - /* Yeah, this is pedantic */ - vftp = the_vft[np->type]; - vftp->generate(np, PRINTFUN_PASS, fp); - fprintf(fp, " return handle;\n"); - fprintf(fp, "}\n\n"); - indent -= 4; - } else { - fprintf(fp, "/***** manual: vl_api_%s_t_print *****/\n\n", - (i8 *) np->data[0]); - } - } - np = np->peer; - } - fprintf(fp, "#endif /* vl_printfun */\n\n"); -} - -void generate_endianfun(YYSTYPE a1, FILE *fp) -{ - node_t *np = (node_t *)a1; - node_vft_t *vftp; - - fprintf(fp, "\n/****** Endian swap functions *****/\n\n"); - fprintf(fp, "#ifdef vl_endianfun\n\n"); - fprintf(fp, "#undef clib_net_to_host_uword\n"); - fprintf(fp, "#ifdef LP64\n"); - fprintf(fp, "#define clib_net_to_host_uword clib_net_to_host_u64\n"); - fprintf(fp, "#else\n"); - fprintf(fp, "#define clib_net_to_host_uword clib_net_to_host_u32\n"); - fprintf(fp, "#endif\n\n"); - - /* Walk the top-level node-list */ - while (np) { - if (np->type == NODE_DEFINE) { - if (!(np->flags & NODE_FLAG_MANUAL_ENDIAN)) { - fprintf(fp, - "static inline void vl_api_%s_t_endian (vl_api_%s_t *a)\n{\n", - (i8 *) np->data[0], (i8 *) np->data[0]); - indent += 4; - /* Yeah, this is pedantic */ - vftp = the_vft[np->type]; - vftp->generate(np, ENDIANFUN_PASS, fp); - fprintf(fp, "}\n\n"); - indent -= 4; - } else { - fprintf(fp, "/***** manual: vl_api_%s_t_endian *****/\n\n", - (i8 *) np->data[0]); - } - } - np = np->peer; - } - fprintf(fp, "#endif /* vl_endianfun */\n\n"); -} - -void add_msg_ids(YYSTYPE a1) -{ - node_t *np = (node_t *)a1; - node_t *new_u16; - node_t *new_vbl; - - /* Walk the top-level node-list */ - while (np) { - if (np->type == NODE_DEFINE) { - if (!(np->flags & NODE_FLAG_TYPEONLY)) { - /* add the parse tree for "u16 _vl_msg_id" */ - new_u16 = make_node(NODE_U16); - new_u16->peer = np->deeper; - np->deeper = new_u16; - new_vbl = make_node(NODE_SCALAR); - new_vbl->data[0] = sxerox("_vl_msg_id"); - new_u16->deeper = new_vbl; - } - } - np = np->peer; - } -} - -/* - * add_scalar_vbl (char *name) - */ -YYSTYPE add_version (YYSTYPE a1, YYSTYPE a2, YYSTYPE a3) -{ - node_t *np; - - np = make_node(NODE_VERSION); - np->data[0] = (void *) a1; - np->data[1] = (void *) a2; - np->data[2] = (void *) a3; - return ((YYSTYPE) np); -} - -void generate_python_msg_definitions(YYSTYPE a1, FILE *fp) -{ - node_t *np = (node_t *)a1; - node_vft_t *vftp; - fprintf (fp, "messages = [\n"); - /* Walk the top-level node-list */ - while (np) { - if (np->type == NODE_DEFINE && !(np->flags & NODE_FLAG_TYPEONLY)) { - /* Yeah, this is pedantic */ - vftp = the_vft[np->type]; - vftp->generate(np, PYTHON_PASS, fp); - } - np = np->peer; - } - fprintf (fp, "\n]\n"); -} - -static bool -is_typeonly_check(node_t *np, bool typeonly) -{ - bool is_typeonly = (np->flags & NODE_FLAG_TYPEONLY); - return (is_typeonly == typeonly); -} - -static void -generate_json_definitions(YYSTYPE a1, FILE *fp, bool typeonly) -{ - node_t *np = (node_t *)a1; - node_vft_t *vftp; - indent_me(fp); - if (typeonly) - fprintf (fp, "\"types\" : [\n"); - else - fprintf (fp, "\"messages\" : [\n"); - - /* Walk the top-level node-list */ - bool comma = false; - indent += 4; - while (np) { - if (np->type == NODE_DEFINE && is_typeonly_check(np, typeonly)) { - /* Yeah, this is pedantic */ - vftp = the_vft[np->type]; - indent_me(fp); - vftp->generate(np, JSON_PASS, fp); - comma = true; - } - np = np->peer; - if (comma && np && - np->type == NODE_DEFINE && is_typeonly_check(np, typeonly)) - fprintf (fp, ",\n"); - - } - indent -= 4; - fprintf (fp, "\n"); - indent_me(fp); - fprintf(fp, "]"); -} - -void generate_python_typeonly_definitions(YYSTYPE a1, FILE *fp) -{ - node_t *np = (node_t *)a1; - node_vft_t *vftp; - fprintf (fp, "types = [\n"); - /* Walk the top-level node-list */ - while (np) { - if (np->type == NODE_DEFINE && (np->flags & NODE_FLAG_TYPEONLY)) { - vftp = the_vft[np->type]; - vftp->generate(np, PYTHON_PASS, fp); - } - np = np->peer; - } - fprintf (fp, "\n]\n"); -} - -void generate_python(YYSTYPE a1, FILE *fp) -{ - generate_python_typeonly_definitions(a1, fp); - generate_python_msg_definitions(a1, fp); - - /* - * API CRC signature - */ - fprintf (fp, "vl_api_version = 0x%08x\n\n", (unsigned int)input_crc); -} - -void generate_json(YYSTYPE a1, FILE *fp) -{ - fprintf (fp, "{\n"); - indent += 4; - generate_json_definitions(a1, fp, true); - fprintf (fp, ",\n"); - generate_json_definitions(a1, fp, false); - - /* - * API CRC signature - */ - fprintf (fp, ",\n\"vl_api_version\" :\"0x%08x\"\n", - (unsigned int)input_crc); - fprintf (fp, "}\n"); -} - -void generate_version_tuple(YYSTYPE a1, FILE *fp) -{ - node_t *this = (node_t *)a1; - - fprintf(fp, "/****** Version tuple *****/\n\n"); - - fprintf(fp, "#ifdef vl_api_version_tuple\n\n"); - - /* Walk the top-level node-list */ - while (this) { - if (this->type == NODE_VERSION) { - fprintf (fp, "vl_api_version_tuple(%s, %d, %d, %d)\n", - fixed_name, IDATA0, IDATA1, IDATA2); - } - this = this->peer; - } - - fprintf(fp, "\n#endif /* vl_api_version_tuple */\n\n"); -} - -void generate(YYSTYPE a1) -{ - if (dump_tree) { - dump((node_t *)a1); - } - - add_msg_ids(a1); - - if (ofp) { - generate_top_boilerplate(ofp); - - generate_msg_ids(a1, ofp); - generate_msg_names(a1, ofp); - generate_msg_name_crc_list(a1, ofp); - generate_typedefs(a1, ofp); - generate_uniondefs(a1, ofp); - generate_printfun(a1, ofp); - generate_endianfun(a1, ofp); - generate_version_tuple(a1, ofp); - - generate_bottom_boilerplate(ofp); - } - if (pythonfp) { - generate_python(a1, pythonfp); - } - if (jsonfp) { - generate_json(a1, jsonfp); - } -} diff --git a/src/tools/vppapigen/node.h b/src/tools/vppapigen/node.h deleted file mode 100644 index 58570d8d096..00000000000 --- a/src/tools/vppapigen/node.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - *------------------------------------------------------------------ - * node.h - definitions for an API generator - * - * Copyright (c) 2004-2009 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------ - */ - -#ifndef _node_h_ -#define _node_h_ - -/* - * Global prototypes - */ - -char *sxerox (const char *s); - -enum node_subclass { /* WARNING: indices must match the vft... */ - NODE_ILLEGAL=0, - NODE_U8, - NODE_U16, - NODE_U32, - NODE_U64, - NODE_I8, - NODE_I16, - NODE_I32, - NODE_I64, - NODE_F64, - NODE_PACKED, - NODE_DEFINE, - NODE_UNION, - NODE_SCALAR, - NODE_VECTOR, - NODE_COMPLEX, - NODE_NOVERSION, - NODE_VERSION, - NODE_UWORD, - NODE_N_TYPES, /* number of node types with VFT's */ - - /* pseudo-node(s) used in the lexer keyword table, but - NOT in need of a VFT... */ - NODE_TYPEONLY, - NODE_MANUAL_PRINT, - NODE_MANUAL_ENDIAN, - NODE_DONT_TRACE, - NODE_AUTOREPLY, -}; - -enum passid { - TYPEDEF_PASS=1, - UNION_DEF_PASS, - ENDIANFUN_PASS, - PRINTFUN_PASS, - PYTHON_PASS, - JSON_PASS, -}; - -extern void *make_node (enum node_subclass type); - -typedef struct node_ { - enum node_subclass type; - struct node_ *peer; - struct node_ *deeper; - int flags; - void *data[4]; -} node_t; - -/* To shut up gcc-4.2.x warnings */ -#define CDATA0 ((char *)(this->data[0])) -#define CDATA1 ((char *)(this->data[0])) -#define CDATA2 ((char *)(this->data[2])) -#define CDATA3 ((char *)(this->data[3])) - -#define IDATA0 ((int)(uword)(this->data[0])) -#define IDATA1 ((int)(uword)(this->data[1])) -#define IDATA2 ((int)(uword)(this->data[2])) -#define IDATA3 ((int)(uword)(this->data[3])) - -#define NODE_FLAG_MANUAL_PRINT (1<<0) -#define NODE_FLAG_MANUAL_ENDIAN (1<<1) -#define NODE_FLAG_TYPEONLY (1<<3) -#define NODE_FLAG_DONT_TRACE (1<<4) -#define NODE_FLAG_AUTOREPLY (1<<5) - -typedef struct node_vft_ { - void (*print)(struct node_ *); - void (*generate)(struct node_ *, enum passid id, FILE *ofp); - char *endian_converter; -} node_vft_t; - -#endif /* _node_h */ diff --git a/src/tools/vppapigen/test_vppapigen.py b/src/tools/vppapigen/test_vppapigen.py new file mode 100755 index 00000000000..09187f4c965 --- /dev/null +++ b/src/tools/vppapigen/test_vppapigen.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +import unittest +from vppapigen import VPPAPI, Option, ParseError + +# TODO +# - test parsing of options, typedefs, enums, defines, CRC +# - test JSON, C output + + +class TestVersion(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.parser = VPPAPI() + + def test_version(self): + version_string = 'option version = "1.0.0";' + r = self.parser.parse_string(version_string) + self.assertTrue(isinstance(r[0], Option)) + + +class TestTypedef(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.parser = VPPAPI() + + def test_duplicatetype(self): + test_string = ''' + typeonly define foo1 { u8 dummy; }; + typeonly define foo1 { u8 dummy; }; + ''' + self.assertRaises(KeyError, self.parser.parse_string, test_string) + + +class TestDefine(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.parser = VPPAPI() + + def test_unknowntype(self): + test_string = 'define foo { foobar foo;};' + self.assertRaises(ParseError, self.parser.parse_string, test_string) + test_string = 'define { u8 foo;};' + self.assertRaises(ParseError, self.parser.parse_string, test_string) + + def test_flags(self): + test_string = ''' + manual_print dont_trace manual_endian define foo { u8 foo; }; + ''' + r = self.parser.parse_string(test_string) + self.assertIsNotNone(r) + s = self.parser.process(r) + self.assertIsNotNone(s) + for d in s['defines']: + self.assertTrue(d.dont_trace) + self.assertTrue(d.manual_endian) + self.assertTrue(d.manual_print) + self.assertFalse(d.autoreply) + + test_string = ''' + nonexisting_flag define foo { u8 foo; }; + ''' + self.assertRaises(ParseError, self.parser.parse_string, test_string) + + +class TestService(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.parser = VPPAPI() + + def test_service(self): + test_string = ''' + service foo { rpc foo (show_version) returns (show_version) }; + ''' + r = self.parser.parse_string(test_string) + print('R', r) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/tools/vppapigen/vppapigen b/src/tools/vppapigen/vppapigen new file mode 120000 index 00000000000..97b143f515e --- /dev/null +++ b/src/tools/vppapigen/vppapigen @@ -0,0 +1 @@ +vppapigen.py \ No newline at end of file diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py new file mode 100755 index 00000000000..81f26fe4432 --- /dev/null +++ b/src/tools/vppapigen/vppapigen.py @@ -0,0 +1,745 @@ +#!/usr/bin/env python + +from __future__ import print_function +import ply.lex as lex +import ply.yacc as yacc +import sys +import argparse +import logging +import binascii +import os + +# +# VPP API language +# + +# Global dictionary of new types (including enums) +global_types = {} + + +def global_type_add(name): + '''Add new type to the dictionary of types ''' + type_name = 'vl_api_' + name + '_t' + if type_name in global_types: + raise KeyError('Type is already defined: {}'.format(name)) + global_types[type_name] = True + + +# All your trace are belong to us! +def exception_handler(exception_type, exception, traceback): + print ("%s: %s" % (exception_type.__name__, exception)) + + +# +# Lexer +# +class VPPAPILexer(object): + def __init__(self, filename): + self.filename = filename + + reserved = { + 'service': 'SERVICE', + 'rpc': 'RPC', + 'returns': 'RETURNS', + 'stream': 'STREAM', + 'events': 'EVENTS', + 'define': 'DEFINE', + 'typedef': 'TYPEDEF', + 'enum': 'ENUM', + 'typeonly': 'TYPEONLY', + 'manual_print': 'MANUAL_PRINT', + 'manual_endian': 'MANUAL_ENDIAN', + 'dont_trace': 'DONT_TRACE', + 'autoreply': 'AUTOREPLY', + 'option': 'OPTION', + 'u8': 'U8', + 'u16': 'U16', + 'u32': 'U32', + 'u64': 'U64', + 'i8': 'I8', + 'i16': 'I16', + 'i32': 'I32', + 'i64': 'I64', + 'f64': 'F64', + 'bool': 'BOOL', + 'string': 'STRING', + 'import': 'IMPORT', + 'true': 'TRUE', + 'false': 'FALSE', + } + + tokens = ['STRING_LITERAL', + 'ID', 'NUM'] + list(reserved.values()) + + t_ignore_LINE_COMMENT = '//.*' + + def t_NUM(self, t): + r'0[xX][0-9a-fA-F]+|\d+' + base = 16 if t.value.startswith('0x') else 10 + t.value = int(t.value, base) + return t + + def t_ID(self, t): + r'[a-zA-Z_][a-zA-Z_0-9]*' + # Check for reserved words + t.type = VPPAPILexer.reserved.get(t.value, 'ID') + return t + + # C string + def t_STRING_LITERAL(self, t): + r'\"([^\\\n]|(\\.))*?\"' + t.value = str(t.value).replace("\"", "") + return t + + # C or C++ comment (ignore) + def t_comment(self, t): + r'(/\*(.|\n)*?\*/)|(//.*)' + t.lexer.lineno += t.value.count('\n') + + # Error handling rule + def t_error(self, t): + raise ParseError("Illegal character '{}' ({})" + "in {}: line {}".format(t.value[0], + hex(ord(t.value[0])), + self.filename, + t.lexer.lineno)) + t.lexer.skip(1) + + # Define a rule so we can track line numbers + def t_newline(self, t): + r'\n+' + t.lexer.lineno += len(t.value) + + literals = ":{}[];=.," + + # A string containing ignored characters (spaces and tabs) + t_ignore = ' \t' + + +class Iterator(type): + def __iter__(self): + return self.iter() + + +class Service(): + def __init__(self, caller, reply, events=[], stream=False): + self.caller = caller + self.reply = reply + self.stream = stream + self.events = events + + +class Typedef(): + def __init__(self, name, flags, block): + self.name = name + self.flags = flags + self.block = block + self.crc = binascii.crc32(str(block)) & 0xffffffff + global_type_add(name) + + def __repr__(self): + return self.name + str(self.flags) + str(self.block) + + +class Define(): + def __init__(self, name, flags, block): + self.name = name + self.flags = flags + self.block = block + self.crc = binascii.crc32(str(block)) & 0xffffffff + self.typeonly = False + self.dont_trace = False + self.manual_print = False + self.manual_endian = False + self.autoreply = False + self.singular = False + for f in flags: + if f == 'typeonly': + self.typeonly = True + global_type_add(name) + elif f == 'dont_trace': + self.dont_trace = True + elif f == 'manual_print': + self.manual_print = True + elif f == 'manual_endian': + self.manual_endian = True + elif f == 'autoreply': + self.autoreply = True + + for b in block: + if isinstance(b, Option): + if b[1] == 'singular' and b[2] == 'true': + self.singular = True + block.remove(b) + + def __repr__(self): + return self.name + str(self.flags) + str(self.block) + + +class Enum(): + def __init__(self, name, block, enumtype='u32'): + self.name = name + self.enumtype = enumtype + count = 0 + for i, b in enumerate(block): + if type(b) is list: + count = b[1] + else: + count += 1 + block[i] = [b, count] + + self.block = block + self.crc = binascii.crc32(str(block)) & 0xffffffff + global_type_add(name) + + def __repr__(self): + return self.name + str(self.block) + + +class Import(): + def __init__(self, filename): + self.filename = filename + + # Deal with imports + parser = VPPAPI(filename=filename) + dirlist = dirlist_get() + f = filename + for dir in dirlist: + f = os.path.join(dir, filename) + if os.path.exists(f): + break + with open(f) as fd: + self.result = parser.parse_file(fd, None) + + def __repr__(self): + return self.filename + + +class Option(): + def __init__(self, option): + self.option = option + self.crc = binascii.crc32(str(option)) & 0xffffffff + + def __repr__(self): + return str(self.option) + + def __getitem__(self, index): + return self.option[index] + + +class Array(): + def __init__(self, fieldtype, name, length): + self.type = 'Array' + self.fieldtype = fieldtype + self.fieldname = name + if type(length) is str: + self.lengthfield = length + self.length = 0 + else: + self.length = length + self.lengthfield = None + + def __repr__(self): + return str([self.fieldtype, self.fieldname, self.length, + self.lengthfield]) + + +class Field(): + def __init__(self, fieldtype, name): + self.type = 'Field' + self.fieldtype = fieldtype + self.fieldname = name + + def __repr__(self): + return str([self.fieldtype, self.fieldname]) + + +class Coord(object): + """ Coordinates of a syntactic element. Consists of: + - File name + - Line number + - (optional) column number, for the Lexer + """ + __slots__ = ('file', 'line', 'column', '__weakref__') + + def __init__(self, file, line, column=None): + self.file = file + self.line = line + self.column = column + + def __str__(self): + str = "%s:%s" % (self.file, self.line) + if self.column: + str += ":%s" % self.column + return str + + +class ParseError(Exception): + pass + + +# +# Grammar rules +# +class VPPAPIParser(object): + tokens = VPPAPILexer.tokens + + def __init__(self, filename, logger): + self.filename = filename + self.logger = logger + self.fields = [] + + def _parse_error(self, msg, coord): + raise ParseError("%s: %s" % (coord, msg)) + + def _parse_warning(self, msg, coord): + if self.logger: + self.logger.warning("%s: %s" % (coord, msg)) + + def _coord(self, lineno, column=None): + return Coord( + file=self.filename, + line=lineno, column=column) + + def _token_coord(self, p, token_idx): + """ Returns the coordinates for the YaccProduction object 'p' indexed + with 'token_idx'. The coordinate includes the 'lineno' and + 'column'. Both follow the lex semantic, starting from 1. + """ + last_cr = p.lexer.lexdata.rfind('\n', 0, p.lexpos(token_idx)) + if last_cr < 0: + last_cr = -1 + column = (p.lexpos(token_idx) - (last_cr)) + return self._coord(p.lineno(token_idx), column) + + def p_slist(self, p): + '''slist : stmt + | slist stmt''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = p[1] + [p[2]] + + def p_stmt(self, p): + '''stmt : define + | typedef + | option + | import + | enum + | service''' + p[0] = p[1] + + def p_import(self, p): + '''import : IMPORT STRING_LITERAL ';' ''' + p[0] = Import(p[2]) + + def p_service(self, p): + '''service : SERVICE '{' service_statements '}' ';' ''' + p[0] = p[3] + + def p_service_statements(self, p): + '''service_statements : service_statement + | service_statements service_statement''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = p[1] + [p[2]] + + def p_service_statement(self, p): + '''service_statement : RPC ID RETURNS ID ';' + | RPC ID RETURNS STREAM ID ';' + | RPC ID RETURNS ID EVENTS event_list ';' ''' + if len(p) == 8: + p[0] = Service(p[2], p[4], p[6]) + elif len(p) == 7: + p[0] = Service(p[2], p[5], stream=True) + else: + p[0] = Service(p[2], p[4]) + + def p_event_list(self, p): + '''event_list : events + | event_list events ''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = p[1] + [p[2]] + + def p_event(self, p): + '''events : ID + | ID ',' ''' + p[0] = p[1] + + def p_enum(self, p): + '''enum : ENUM ID '{' enum_statements '}' ';' ''' + p[0] = Enum(p[2], p[4]) + + def p_enum_type(self, p): + ''' enum : ENUM ID ':' enum_size '{' enum_statements '}' ';' ''' + if len(p) == 9: + p[0] = Enum(p[2], p[6], enumtype=p[4]) + else: + p[0] = Enum(p[2], p[4]) + + def p_enum_size(self, p): + ''' enum_size : U8 + | U16 + | U32 ''' + p[0] = p[1] + + def p_define(self, p): + '''define : DEFINE ID '{' block_statements_opt '}' ';' ''' + self.fields = [] + p[0] = Define(p[2], [], p[4]) + + def p_define_flist(self, p): + '''define : flist DEFINE ID '{' block_statements_opt '}' ';' ''' + p[0] = Define(p[3], p[1], p[5]) + + def p_flist(self, p): + '''flist : flag + | flist flag''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = p[1] + [p[2]] + + def p_flag(self, p): + '''flag : MANUAL_PRINT + | MANUAL_ENDIAN + | DONT_TRACE + | TYPEONLY + | AUTOREPLY''' + if len(p) == 1: + return + p[0] = p[1] + + def p_typedef(self, p): + '''typedef : TYPEDEF ID '{' block_statements_opt '}' ';' ''' + p[0] = Typedef(p[2], [], p[4]) + + def p_block_statements_opt(self, p): + '''block_statements_opt : block_statements''' + p[0] = p[1] + + def p_block_statements(self, p): + '''block_statements : block_statement + | block_statements block_statement''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = p[1] + [p[2]] + + def p_block_statement(self, p): + '''block_statement : declaration + | option ''' + p[0] = p[1] + + def p_enum_statements(self, p): + '''enum_statements : enum_statement + | enum_statements enum_statement''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = p[1] + [p[2]] + + def p_enum_statement(self, p): + '''enum_statement : ID '=' NUM ',' + | ID ',' ''' + if len(p) == 5: + p[0] = [p[1], p[3]] + else: + p[0] = p[1] + + def p_declaration(self, p): + '''declaration : type_specifier ID ';' ''' + if len(p) != 4: + self._parse_error('ERROR') + self.fields.append(p[2]) + p[0] = Field(p[1], p[2]) + + def p_declaration_array(self, p): + '''declaration : type_specifier ID '[' NUM ']' ';' + | type_specifier ID '[' ID ']' ';' ''' + if len(p) != 7: + return self._parse_error( + 'array: %s' % p.value, + self._coord(lineno=p.lineno)) + + # Make this error later + if type(p[4]) is int and p[4] == 0: + # XXX: Line number is wrong + self._parse_warning('Old Style VLA: {} {}[{}];' + .format(p[1], p[2], p[4]), + self._token_coord(p, 1)) + + if type(p[4]) is str and p[4] not in self.fields: + # Verify that length field exists + self._parse_error('Missing length field: {} {}[{}];' + .format(p[1], p[2], p[4]), + self._token_coord(p, 1)) + p[0] = Array(p[1], p[2], p[4]) + + def p_option(self, p): + '''option : OPTION ID '=' assignee ';' ''' + p[0] = Option([p[1], p[2], p[4]]) + + def p_assignee(self, p): + '''assignee : NUM + | TRUE + | FALSE + | STRING_LITERAL ''' + p[0] = p[1] + + def p_type_specifier(self, p): + '''type_specifier : U8 + | U16 + | U32 + | U64 + | I8 + | I16 + | I32 + | I64 + | F64 + | BOOL + | STRING''' + p[0] = p[1] + + # Do a second pass later to verify that user defined types are defined + def p_typedef_specifier(self, p): + '''type_specifier : ID ''' + if p[1] not in global_types: + self._parse_error('Undefined type: {}'.format(p[1]), + self._token_coord(p, 1)) + p[0] = p[1] + + # Error rule for syntax errors + def p_error(self, p): + if p: + self._parse_error( + 'before: %s' % p.value, + self._coord(lineno=p.lineno)) + else: + self._parse_error('At end of input', self.filename) + + +class VPPAPI(object): + + def __init__(self, debug=False, filename='', logger=None): + self.lexer = lex.lex(module=VPPAPILexer(filename), debug=debug) + self.parser = yacc.yacc(module=VPPAPIParser(filename, logger), + tabmodule='vppapigentab', debug=debug) + self.logger = logger + + def parse_string(self, code, debug=0, lineno=1): + self.lexer.lineno = lineno + return self.parser.parse(code, lexer=self.lexer, debug=debug) + + def parse_file(self, fd, debug=0): + data = fd.read() + return self.parse_string(data, debug=debug) + + def autoreply_block(self, name): + block = [Field('u32', 'context'), + Field('i32', 'retval')] + return Define(name + '_reply', [], block) + + def process(self, objs): + s = {} + s['defines'] = [] + s['typedefs'] = [] + s['imports'] = [] + s['options'] = {} + s['enums'] = [] + s['services'] = [] + + for o in objs: + if isinstance(o, Define): + if o.typeonly: + s['typedefs'].append(o) + else: + s['defines'].append(o) + if o.autoreply: + s['defines'].append(self.autoreply_block(o.name)) + elif isinstance(o, Option): + s['options'][o[1]] = o[2] + elif isinstance(o, Enum): + s['enums'].append(o) + elif isinstance(o, Typedef): + s['typedefs'].append(o) + elif type(o) is list: + for o2 in o: + if isinstance(o2, Service): + s['services'].append(o2) + + # Create services implicitly + msgs = {d.name: d for d in s['defines']} + svcs = {s.caller: s for s in s['services']} + + for service in svcs: + if service not in msgs: + raise ValueError('Service definition refers to unknown message' + ' definition: {}'.format(service)) + if svcs[service].reply not in msgs: + raise ValueError('Service definition refers to unknown message' + ' definition in reply: {}' + .format(svcs[service].reply)) + for event in svcs[service].events: + if event not in msgs: + raise ValueError('Service definition refers to unknown ' + 'event: {} in message: {}' + .format(event, service)) + + for d in msgs: + if msgs[d].singular is True: + continue + if d.endswith('_counters'): + continue + if d.endswith('_reply'): + if d[:-6] in svcs: + continue + if d[:-6] not in msgs: + self.logger.warning('{} missing calling message' + .format(d)) + continue + if d.endswith('_dump'): + if d in svcs: + continue + if d[:-5]+'_details' in msgs: + s['services'].append(Service(d, d[:-5]+'_details', + stream=True)) + else: + self.logger.error('{} missing details message' + .format(d)) + continue + + if d.endswith('_details'): + if d[:-8]+'_dump' not in msgs: + self.logger.error('{} missing dump message' + .format(d)) + continue + + if d in svcs: + continue + if d+'_reply' in msgs: + s['services'].append(Service(d, d+'_reply')) + else: + self.logger.warning('{} missing reply message ({})' + .format(d, d+'_reply')) + s['services'].append(Service(d, None)) + + return s + + def process_imports(self, objs): + for o in objs: + if isinstance(o, Import): + return objs + self.process_imports(o.result) + return objs + + +# Add message ids to each message. +def add_msg_id(s): + for o in s: + o.block.insert(0, Field('u16', '_vl_msg_id')) + return s + + +def getcrc(s): + return binascii.crc32(str(s)) & 0xffffffff + + +dirlist = [] + + +def dirlist_add(dirs): + global dirlist + if dirs: + dirlist = dirlist + dirs + + +def dirlist_get(): + return dirlist + + +# +# Main +# +def main(): + logging.basicConfig() + log = logging.getLogger('vppapigen') + + cliparser = argparse.ArgumentParser(description='VPP API generator') + cliparser.add_argument('--pluginpath', default=""), + cliparser.add_argument('--includedir', action='append'), + cliparser.add_argument('--input', type=argparse.FileType('r'), + default=sys.stdin) + cliparser.add_argument('--output', nargs='?', type=argparse.FileType('w'), + default=sys.stdout) + + cliparser.add_argument('output_module', nargs='?', default='C') + cliparser.add_argument('--debug', action='store_true') + cliparser.add_argument('--show-name', nargs=1) + args = cliparser.parse_args() + + dirlist_add(args.includedir) + if not args.debug: + sys.excepthook = exception_handler + + # Filename + if args.show_name: + filename = args.show_name[0] + elif args.input != sys.stdin: + filename = args.input.name + else: + filename = '' + + parser = VPPAPI(debug=args.debug, filename=filename, logger=log) + result = parser.parse_file(args.input, log) + + # Build a list of objects. Hash of lists. + result = parser.process_imports(result) + s = parser.process(result) + + # Add msg_id field + s['defines'] = add_msg_id(s['defines']) + + file_crc = getcrc(s) + + # + # Debug + if args.debug: + import pprint + pp = pprint.PrettyPrinter(indent=4) + for t in s['defines']: + pp.pprint([t.name, t.flags, t.block]) + for t in s['typedefs']: + pp.pprint([t.name, t.flags, t.block]) + + # + # Generate representation + # + import imp + + # Default path + if not args.pluginpath: + pluginpath = os.path.dirname(os.path.realpath(__file__)) + \ + '/../share/vpp/' + else: + pluginpath = args.pluginpath + '/' + module_path = pluginpath + args.output_module + '.py' + + try: + plugin = imp.load_source(args.output_module, module_path) + except Exception, err: + raise Exception('Error importing output plugin: {}, {}' + .format(module_path, err)) + + result = plugin.run(filename, s, file_crc) + if result: + print (result, file=args.output) + else: + raise Exception('Running plugin failed: {} {}' + .format(filename, result)) + + +if __name__ == '__main__': + main() -- cgit 1.2.3-korg