mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
374 lines
7.5 KiB
C++
374 lines
7.5 KiB
C++
#include "CodeEditor.h"
|
|
|
|
namespace Upp {
|
|
|
|
const wchar *eatstring(const wchar *s) {
|
|
int delim = *s++;
|
|
while(*s)
|
|
if(*s == '\\' && s[1])
|
|
s += 2;
|
|
else
|
|
if(*s == delim)
|
|
return s + 1;
|
|
else
|
|
s++;
|
|
return s;
|
|
}
|
|
|
|
inline const wchar *strnext(const wchar *p, const wchar *end, int ch) {
|
|
while(p < end) {
|
|
if(*p == ch) return p;
|
|
p++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CSyntax::ClearBraces() {
|
|
cl = bl = pl = 0;
|
|
spar = 0;
|
|
brk.Clear();
|
|
blk.Clear();
|
|
bid.Clear();
|
|
bid.Add(0);
|
|
par.Clear();
|
|
}
|
|
|
|
void CSyntax::Clear() {
|
|
ClearBraces();
|
|
linecont = linecomment = comment = string = false;
|
|
macro = MACRO_OFF;
|
|
stmtline = endstmtline = seline = -1;
|
|
was_namespace = false;
|
|
raw_string.Clear();
|
|
ifstack.Clear();
|
|
}
|
|
|
|
const wchar *isstmt(const wchar *p) {
|
|
static const char *stmt[] = {
|
|
"if", "else", "while", "do", "for"
|
|
};
|
|
for(const char **q = stmt; q < stmt + __countof(stmt); q++) {
|
|
const char *k = *q;
|
|
const wchar *s = p;
|
|
for(;;) {
|
|
if(*k== '\0') {
|
|
if(!iscidl(*s)) return s;
|
|
break;
|
|
}
|
|
if(*s != *k)
|
|
break;
|
|
s++;
|
|
k++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static WString sReadLn(const wchar *p)
|
|
{
|
|
WStringBuffer wbuf;
|
|
while(*p && *p <= ' ')
|
|
p++;
|
|
while(*p && !(*p == '/' && (p[1] == '/' || p[1] == '*'))) {
|
|
if(*p <= ' ') {
|
|
while(*++p && *p <= ' ')
|
|
;
|
|
if(*p && !(*p == '/' && (p[1] == '/' || p[1] == '*')))
|
|
wbuf.Cat(' ');
|
|
}
|
|
else
|
|
wbuf.Cat(*p++);
|
|
}
|
|
return WString(wbuf);
|
|
}
|
|
|
|
int LastC(const wchar *b, const wchar *e)
|
|
{
|
|
if(e == b)
|
|
return 0;
|
|
return *e;
|
|
}
|
|
|
|
void CSyntax::Grounding(const wchar *b, const wchar *e)
|
|
{
|
|
if(b >= e || comment || !iscib(*b))
|
|
return;
|
|
e--;
|
|
while(e > b && (*e == '\t' || *e == ' ' ))
|
|
e--;
|
|
if(*e != ':')
|
|
ClearBraces();
|
|
}
|
|
|
|
void CSyntax::ScanSyntax(const wchar *ln, const wchar *e, int line, int tab_size)
|
|
{
|
|
CTIMING("ScanSyntax");
|
|
Grounding(ln, e);
|
|
if(!linecont) {
|
|
linecomment = false;
|
|
string = false;
|
|
}
|
|
if(macro != CSyntax::MACRO_CONT)
|
|
macro = CSyntax::MACRO_OFF;
|
|
linecont = e > ln && e[-1] == '\\';
|
|
const wchar *p = ln;
|
|
int lindent = 0, pos = 0;
|
|
while(p < e && (*p == '\t' || *p == ' ')) {
|
|
if(*p++ == '\t' || ++pos >= tab_size) {
|
|
pos = 0;
|
|
lindent++;
|
|
}
|
|
}
|
|
if(!comment && *p == '#') {
|
|
while(++p < e && (*p == ' ' || *p == '\t'))
|
|
p++;
|
|
const wchar *id = p;
|
|
while(p < e && iscidl(*p))
|
|
p++;
|
|
int idlen = int(p - id);
|
|
if(id[0] == 'i' && id[1] == 'f'
|
|
&& (idlen == 2 || idlen == 5 && id[2] == 'd' && id[3] == 'e' && id[4] == 'f'
|
|
|| idlen == 6 && id[2] == 'n' && id[3] == 'd' && id[4] == 'e' && id[5] == 'f')) {
|
|
IfState& ifstate = ifstack.Add();
|
|
ifstate.state = IfState::IF;
|
|
ifstate.iftext = sReadLn(ln);
|
|
ifstate.ifline = line + 1;
|
|
}
|
|
switch(idlen)
|
|
{
|
|
case 6:
|
|
if(id[0] == 'd' && id[1] == 'e' && id[2] == 'f' && id[3] == 'i' && id[4] == 'n' && id[5] == 'e')
|
|
macro = CSyntax::MACRO_CONT;
|
|
break;
|
|
|
|
case 4:
|
|
if(id[0] == 'e' && id[1] == 'l') {
|
|
if(id[2] == 'i' && id[3] == 'f') {
|
|
if(ifstack.GetCount() == 0) {
|
|
IfState& ifstate = ifstack.Add();
|
|
ifstate.ifline = 0;
|
|
ifstate.state = IfState::ELSE_ERROR;
|
|
}
|
|
else {
|
|
IfState& ifstate = ifstack.Top();
|
|
if(ifstate.state == IfState::IF || ifstate.state == IfState::ELIF) {
|
|
ifstate.state = IfState::ELIF;
|
|
ifstate.iftext = WString().Cat() << sReadLn(ln) << ", " << ifstate.iftext;
|
|
}
|
|
else
|
|
ifstate.state = IfState::ELSE_ERROR;
|
|
}
|
|
}
|
|
else
|
|
if(id[2] == 's' && id[3] == 'e') {
|
|
if(ifstack.GetCount() == 0) {
|
|
IfState& ifstate = ifstack.Add();
|
|
ifstate.ifline = 0;
|
|
ifstate.state = IfState::ELSE_ERROR;
|
|
}
|
|
else {
|
|
IfState& ifstate = ifstack.Top();
|
|
if(ifstate.state == IfState::IF || ifstate.state == IfState::ELIF) {
|
|
ifstate.state = IfState::ELSE;
|
|
ifstate.iftext = "#else, " + ifstate.iftext;
|
|
}
|
|
else
|
|
ifstate.state = IfState::ELSE_ERROR;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
if(id[0] == 'e' && id[1] == 'n' && id[2] == 'd' && id[3] == 'i' && id[4] == 'f')
|
|
{
|
|
int itop = ifstack.GetCount() - 1;
|
|
if(itop < 0) {
|
|
IfState& ifstate = ifstack.Add();
|
|
ifstate.ifline = 0;
|
|
ifstate.state = IfState::ENDIF_ERROR;
|
|
}
|
|
else if(ifstack[itop].state != IfState::ENDIF_ERROR)
|
|
ifstack.Trim(itop);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(macro == CSyntax::MACRO_CONT && !(p < e && e[-1] == '\\'))
|
|
macro = CSyntax::MACRO_END;
|
|
for(;;) {
|
|
if(comment) {
|
|
p = strnext(p, e, '*');
|
|
if(!p) break;
|
|
if(*++p == '/') {
|
|
comment = false;
|
|
p++;
|
|
}
|
|
}
|
|
else {
|
|
int pc = 0;
|
|
for(;;) {
|
|
int raw_n;
|
|
if(p >= e) return;
|
|
const wchar *pp;
|
|
if(!iscidl(pc) && (pp = isstmt(p)) != NULL) {
|
|
stmtline = line;
|
|
spar = 0;
|
|
pc = 0;
|
|
p = pp;
|
|
}
|
|
else
|
|
if(RawString(p, raw_n))
|
|
p += raw_n;
|
|
else
|
|
if(raw_string.GetCount()) {
|
|
const wchar *s = p;
|
|
const wchar *r = raw_string;
|
|
while(*s && *r) {
|
|
if(*s != *r)
|
|
break;
|
|
s++;
|
|
r++;
|
|
}
|
|
if(*r == '\0') {
|
|
p = s;
|
|
raw_string.Clear();
|
|
}
|
|
else
|
|
p++;
|
|
if(p >= e) return;
|
|
}
|
|
else
|
|
if(!iscidl(pc) && p[0] == 'n' && p[1] == 'a' && p[2] == 'm' && p[3] == 'e' &&
|
|
p[4] == 's' && p[5] == 'p' && p[6] == 'a' && p[7] == 'c' && p[8] == 'e' &&
|
|
!iscidl(p[9])) {
|
|
was_namespace = true;
|
|
p += 9;
|
|
}
|
|
else {
|
|
int c = *p++;
|
|
if(c == '/') break;
|
|
if(c == '\'' || c == '\"') {
|
|
p = eatstring(p - 1);
|
|
if(p >= e) {
|
|
string = true;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
if(c == ';' && spar == 0) {
|
|
seline = stmtline;
|
|
endstmtline = line;
|
|
stmtline = -1;
|
|
was_namespace = false;
|
|
}
|
|
else
|
|
if(c == '{') {
|
|
if(was_namespace) {
|
|
brk.Add(0);
|
|
was_namespace = false;
|
|
}
|
|
else {
|
|
cl++;
|
|
brk.Add('}');
|
|
bid.Add(lindent + 1);
|
|
}
|
|
blk.Add() = line;
|
|
stmtline = -1;
|
|
par.Clear();
|
|
}
|
|
else
|
|
if(c == '}') {
|
|
if(brk.GetCount()) {
|
|
if(brk.Top()) {
|
|
cl--;
|
|
if(bid.GetCount() > 1)
|
|
bid.Drop();
|
|
}
|
|
brk.Drop();
|
|
}
|
|
if(blk.GetCount())
|
|
blk.Drop();
|
|
stmtline = -1;
|
|
par.Clear();
|
|
}
|
|
else
|
|
if(c == '(') {
|
|
pl++;
|
|
brk.Add(')');
|
|
Isx& m = par.Add();
|
|
m.line = line;
|
|
m.pos = int(p - ln);
|
|
spar++;
|
|
}
|
|
else
|
|
if(c == '[') {
|
|
bl++;
|
|
brk.Add(']');
|
|
Isx& m = par.Add();
|
|
m.line = line;
|
|
m.pos = int(p - ln);
|
|
spar++;
|
|
}
|
|
else
|
|
if(c == ')') {
|
|
if(brk.GetCount()) {
|
|
pl--;
|
|
brk.Drop();
|
|
}
|
|
if(par.GetCount())
|
|
par.Drop();
|
|
spar--;
|
|
}
|
|
else
|
|
if(c == ']') {
|
|
if(brk.GetCount()) {
|
|
bl--;
|
|
brk.Drop();
|
|
}
|
|
if(par.GetCount())
|
|
par.Drop();
|
|
spar--;
|
|
}
|
|
pc = c;
|
|
}
|
|
}
|
|
if(*p == '/') {
|
|
linecomment = true;
|
|
return;
|
|
}
|
|
if(*p == '*') {
|
|
comment = true;
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSyntax::Serialize(Stream& s)
|
|
{
|
|
s % comment;
|
|
s % linecomment;
|
|
s % string;
|
|
s % linecont;
|
|
s % was_namespace;
|
|
s % macro;
|
|
s % raw_string;
|
|
|
|
s % cl % bl % pl;
|
|
|
|
s % brk;
|
|
s % blk;
|
|
s % bid;
|
|
s % par;
|
|
s % ifstack;
|
|
|
|
s % stmtline;
|
|
s % endstmtline;
|
|
s % seline;
|
|
s % spar;
|
|
|
|
s % highlight;
|
|
};
|
|
|
|
}
|