📄 jscript.c
字号:
/* * $Id: jscript.c 587 2007-07-16 14:24:10Z dfishburn $ * * Copyright (c) 2003, Darren Hiebert * * This source code is released for free distribution under the terms of the * GNU General Public License. * * This module contains functions for generating tags for JavaScript language * files. * * This is a good reference for different forms of the function statement: * http://www.permadi.com/tutorial/jsFunc/ *//* * INCLUDE FILES */#include "general.h" /* must always come first */#include <ctype.h> /* to define isalpha () */#include <setjmp.h>#ifdef DEBUG#include <stdio.h>#endif#include "debug.h"#include "entry.h"#include "keyword.h"#include "parse.h"#include "read.h"#include "routines.h"#include "vstring.h"/* * MACROS */#define isType(token,t) (boolean) ((token)->type == (t))#define isKeyword(token,k) (boolean) ((token)->keyword == (k))/* * DATA DECLARATIONS */typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;/* * Tracks class and function names already created */static stringList *ClassNames;static stringList *FunctionNames;/* Used to specify type of keyword.*/typedef enum eKeywordId { KEYWORD_NONE = -1, KEYWORD_function, KEYWORD_capital_function, KEYWORD_object, KEYWORD_capital_object, KEYWORD_prototype, KEYWORD_var, KEYWORD_new, KEYWORD_this, KEYWORD_for, KEYWORD_while, KEYWORD_do, KEYWORD_if, KEYWORD_else, KEYWORD_switch, KEYWORD_try, KEYWORD_catch, KEYWORD_finally} keywordId;/* Used to determine whether keyword is valid for the token language and * what its ID is. */typedef struct sKeywordDesc { const char *name; keywordId id;} keywordDesc;typedef enum eTokenType { TOKEN_UNDEFINED, TOKEN_CHARACTER, TOKEN_CLOSE_PAREN, TOKEN_SEMICOLON, TOKEN_COLON, TOKEN_COMMA, TOKEN_KEYWORD, TOKEN_OPEN_PAREN, TOKEN_OPERATOR, TOKEN_IDENTIFIER, TOKEN_STRING, TOKEN_PERIOD, TOKEN_OPEN_CURLY, TOKEN_CLOSE_CURLY, TOKEN_EQUAL_SIGN, TOKEN_FORWARD_SLASH, TOKEN_OPEN_SQUARE, TOKEN_CLOSE_SQUARE} tokenType;typedef struct sTokenInfo { tokenType type; keywordId keyword; vString * string; vString * scope; unsigned long lineNumber; fpos_t filePosition; int nestLevel; boolean ignoreTag;} tokenInfo;/* * DATA DEFINITIONS */static langType Lang_js;static jmp_buf Exception;typedef enum { JSTAG_FUNCTION, JSTAG_CLASS, JSTAG_METHOD, JSTAG_VARIABLE, JSTAG_COUNT} jsKind;static kindOption JsKinds [] = { { TRUE, 'f', "function", "functions" }, { TRUE, 'c', "class", "classes" }, { TRUE, 'm', "method", "methods" }, { TRUE, 'v', "variable", "global variables" }};static const keywordDesc JsKeywordTable [] = { /* keyword keyword ID */ { "function", KEYWORD_function }, { "Function", KEYWORD_capital_function }, { "object", KEYWORD_object }, { "Object", KEYWORD_capital_object }, { "prototype", KEYWORD_prototype }, { "var", KEYWORD_var }, { "new", KEYWORD_new }, { "this", KEYWORD_this }, { "for", KEYWORD_for }, { "while", KEYWORD_while }, { "do", KEYWORD_do }, { "if", KEYWORD_if }, { "else", KEYWORD_else }, { "switch", KEYWORD_switch }, { "try", KEYWORD_try }, { "catch", KEYWORD_catch }, { "finally", KEYWORD_finally }};/* * FUNCTION DEFINITIONS *//* Recursive functions */static void parseFunction (tokenInfo *const token);static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent);static boolean parseLine (tokenInfo *const token, boolean is_inside_class);static boolean isIdentChar1 (const int c){ /* * Other databases are less restrictive on the first character of * an identifier. * isIdentChar1 is used to identify the first character of an * identifier, so we are removing some restrictions. */ return (boolean) (isalpha (c) || c == '@' || c == '_' );}static boolean isIdentChar (const int c){ return (boolean) (isalpha (c) || isdigit (c) || c == '$' || c == '@' || c == '_' || c == '#');}static void buildJsKeywordHash (void){ const size_t count = sizeof (JsKeywordTable) / sizeof (JsKeywordTable [0]); size_t i; for (i = 0 ; i < count ; ++i) { const keywordDesc* const p = &JsKeywordTable [i]; addKeyword (p->name, Lang_js, (int) p->id); }}static tokenInfo *newToken (void){ tokenInfo *const token = xMalloc (1, tokenInfo); token->type = TOKEN_UNDEFINED; token->keyword = KEYWORD_NONE; token->string = vStringNew (); token->scope = vStringNew (); token->nestLevel = 0; token->ignoreTag = FALSE; return token;}static void deleteToken (tokenInfo *const token){ vStringDelete (token->string); vStringDelete (token->scope); eFree (token);}/* * Tag generation functions */static void makeConstTag (tokenInfo *const token, const jsKind kind){ if (JsKinds [kind].enabled && ! token->ignoreTag ) { const char *const name = vStringValue (token->string); tagEntryInfo e; initTagEntry (&e, name); e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; e.kindName = JsKinds [kind].name; e.kind = JsKinds [kind].letter; makeTagEntry (&e); }}static void makeJsTag (tokenInfo *const token, const jsKind kind){ vString * fulltag; if (JsKinds [kind].enabled && ! token->ignoreTag ) { /* * If a scope has been added to the token, change the token * string to include the scope when making the tag. */ if ( vStringLength(token->scope) > 0 ) { fulltag = vStringNew (); vStringCopy(fulltag, token->scope); vStringCatS (fulltag, "."); vStringCatS (fulltag, vStringValue(token->string)); vStringTerminate(fulltag); vStringCopy(token->string, fulltag); vStringDelete (fulltag); } makeConstTag (token, kind); }}static void makeClassTag (tokenInfo *const token){ if ( ! token->ignoreTag ) { if ( ! stringListHas(ClassNames, vStringValue (token->string)) ) { stringListAdd (ClassNames, vStringNewCopy (token->string)); makeJsTag (token, JSTAG_CLASS); } }}static void makeFunctionTag (tokenInfo *const token){ if ( ! token->ignoreTag ) { if ( ! stringListHas(FunctionNames, vStringValue (token->string)) ) { stringListAdd (FunctionNames, vStringNewCopy (token->string)); makeJsTag (token, JSTAG_FUNCTION); } }}/* * Parsing functions */static int skipToCharacter (const int c){ int d; do { d = fileGetc (); } while (d != EOF && d != c); return d;}static void parseString (vString *const string, const int delimiter){ boolean end = FALSE; int c; while (! end) { c = fileGetc (); if (c == EOF) end = TRUE; else if (c == delimiter) end = TRUE; else vStringPut (string, c); } vStringTerminate (string);}/* Read a C identifier beginning with "firstChar" and places it into * "name". */static void parseIdentifier (vString *const string, const int firstChar){ int c = firstChar; Assert (isIdentChar1 (c)); do { vStringPut (string, c); c = fileGetc (); } while (isIdentChar (c)); vStringTerminate (string); if (!isspace (c)) fileUngetc (c); /* unget non-identifier character */}static keywordId analyzeToken (vString *const name){ vString *keyword = vStringNew (); keywordId result; vStringCopyToLower (keyword, name); result = (keywordId) lookupKeyword (vStringValue (keyword), Lang_js); vStringDelete (keyword); return result;}static void readToken (tokenInfo *const token){ int c; token->type = TOKEN_UNDEFINED; token->keyword = KEYWORD_NONE; vStringClear (token->string);getNextChar: do { c = fileGetc (); /* * Added " to the list of ignores, not sure what this * might break but it gets by this issue: * create table "t1" (...) */ } while (c == '\t' || c == ' ' || c == '\n'); switch (c) { case EOF: longjmp (Exception, (int)ExceptionEOF); break; case '(': token->type = TOKEN_OPEN_PAREN; break; case ')': token->type = TOKEN_CLOSE_PAREN; break; case ';': token->type = TOKEN_SEMICOLON; break; case ',': token->type = TOKEN_COMMA; break; case '.': token->type = TOKEN_PERIOD; break; case ':': token->type = TOKEN_COLON; break; case '{': token->type = TOKEN_OPEN_CURLY; break; case '}': token->type = TOKEN_CLOSE_CURLY; break; case '=': token->type = TOKEN_EQUAL_SIGN; break; case '[': token->type = TOKEN_OPEN_SQUARE; break; case ']': token->type = TOKEN_CLOSE_SQUARE; break; case '\'': case '"': token->type = TOKEN_STRING; parseString (token->string, c); token->lineNumber = getSourceLineNumber (); token->filePosition = getInputFilePosition (); break; case '/': { int d = fileGetc (); if ( (d != '*') && /* is this the start of a comment? */ (d != '/') ) /* is a one line comment? */ { token->type = TOKEN_FORWARD_SLASH; fileUngetc (d); } else { if (d == '*') { do { skipToCharacter ('*'); c = fileGetc (); if (c == '/') break; else fileUngetc (c); } while (c != EOF && c != '\0'); goto getNextChar; } else if (d == '/') /* is this the start of a comment? */ { skipToCharacter ('\n'); goto getNextChar; } } break; } default: if (! isIdentChar1 (c)) token->type = TOKEN_UNDEFINED; else { parseIdentifier (token->string, c); token->lineNumber = getSourceLineNumber (); token->filePosition = getInputFilePosition (); token->keyword = analyzeToken (token->string); if (isKeyword (token, KEYWORD_NONE)) token->type = TOKEN_IDENTIFIER; else token->type = TOKEN_KEYWORD; } break; }}static void copyToken (tokenInfo *const dest, tokenInfo *const src){ dest->nestLevel = src->nestLevel; dest->lineNumber = src->lineNumber; dest->filePosition = src->filePosition; dest->type = src->type; dest->keyword = src->keyword; vStringCopy(dest->string, src->string); vStringCopy(dest->scope, src->scope);}/* * Token parsing functions */static void skipArgumentList (tokenInfo *const token){ int nest_level = 0; /* * Other databases can have arguments with fully declared * datatypes: * ( name varchar(30), text binary(10) ) * So we must check for nested open and closing parantheses */ if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */ { nest_level++; while (! (isType (token, TOKEN_CLOSE_PAREN) && (nest_level == 0))) { readToken (token); if (isType (token, TOKEN_OPEN_PAREN)) { nest_level++; } if (isType (token, TOKEN_CLOSE_PAREN)) { if (nest_level > 0) { nest_level--; } } } readToken (token); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -