#include void LambdaArgs(CParser& p, SLambda& l) { p.PassChar('('); if(!p.Char(')')) for(;;) { l.ref.Add(p.Char('&')); l.arg.Add(p.ReadId()); if(p.Char(')')) break; p.PassChar(','); } l.ref.Shrink(); l.arg.Shrink(); } String Sic::ReadName() { if(Id("operator")) { if(Char2('+', '=')) return "operator+="; else if(Char2('-', '=')) return "operator-="; else if(Char2('*', '=')) return "operator*="; else if(Char2('/', '=')) return "operator/="; else if(Char2('%', '=')) return "operator%="; else if(Char2('>', '<')) return "operator><"; else if(Char2('=', '=')) return "operator=="; else if(Char2('<', '<')) return "operator<<"; else ThrowError("invalid operator"); } return ReadId(); } SVal Sic::ReadLambda() { SVal lambda; SLambda& l = lambda.CreateLambda(); LambdaArgs(*this, l); if(!IsChar('{')) ThrowError("missing '{'"); const char *t = GetPtr(); SkipStatement(); l.code = String(t, GetPtr()); return lambda; } SVal Sic::ExecuteLambda(const String& id, SVal lambda, SVal *self, Vector& arg) { if(!lambda.IsLambda()) ThrowError("lambda required"); const SLambda& l = lambda.GetLambda(); if(l.arg.GetCount() != arg.GetCount()) ThrowError("invalid number of arguments in call to '" + id + "'"); if(l.escape) { SVal ret_val = (*l.escape)(*this, arg); return ret_val; } if(stack_level > 40) ThrowError("Stack overflow"); Sic sub(global, l.code); sub.self = self; sub.stack_level = stack_level + 1; for(int i = 0; i < l.arg.GetCount(); i++) { Var& v = sub.var.GetAdd(l.arg[i]); if(l.ref[i]) { if(!arg[i].lvalue) ThrowError("l-value required for " + l.arg[i] + " argument in call to '" + id + "'"); v.ref = arg[i].lvalue; } else v.var = arg[i]; } sub.Run(); return sub.return_value; } Sic::SRVal Sic::Term() { if(IsInt()) return ReadInt(); if(IsString()) return ReadString(); if(Char('@')) return ReadLambda(); SRVal v; if(Id("void")) return v; if(Char('{')) { SVal map; map.SetMap(); if(!Char('}')) for(;;) { SVal v = Exp(); if(Char(':')) map.Set(v) = Exp(); else map.Add() = v; if(Char('}')) break; PassChar(','); } return map; } if(Char('(')) { SRVal v = Exp(); PassChar(')'); return v; } SVal *_self = NULL; bool _global = false; if(Char('.')) { if(!self) ThrowError("member-access in non-member lambda"); _self = self; } else if(Char(':')) _global = true; if(IsId()) { String id = ReadName(); int ii; SVal *self = _self; if(!_self && !_global && var.Find(id) < 0 && IsChar('(') && (ii = global.Find(id)) >= 0 && global[ii].IsLambda()) v = global[ii]; else { if(_self) v.lvalue = &_self->Set(id); else if(_global) v.lvalue = &global.GetAdd(id); else { Var& q = var.GetAdd(id); if(q.ref) v.lvalue = q.ref; else v.lvalue = &q.var; } for(;;) if(Char('[')) { self = v.lvalue; if(Char(']')) v.lvalue = &v.lvalue->Add(); else { SVal x = Exp(); v.lvalue = &v.lvalue->Set(x); PassChar(']'); } } else if(Char('.')) { self = v.lvalue; id = ReadId(); v.lvalue = &v.lvalue->Set(id); } else break; } if(Char('(')) { Vector arg; if(!Char(')')) for(;;) { arg.Add(Exp()); if(Char(')')) break; PassChar(','); } v = ExecuteLambda(id, v, self, arg); } } else ThrowError("invalid expression"); return v; } void Escape(ArrayMap& global, const char *function, SVal (*fn)(Sic& sic, Vector& var)) { CParser p(function); SVal& v = global.GetAdd(p.ReadId()); SLambda& l = v.CreateLambda(); l.escape = fn; LambdaArgs(p, l); } Sic::SRVal Sic::Unary() { SRVal a; if(Char2('+', '+')) { a = Unary(); if(!a->IsNumber()) ThrowError("invalid type for prefix '++' operator"); if(!a.lvalue) ThrowError("l-value required for prefix '++' operator"); *a.lvalue = a->GetNumber() + 1; } else if(Char2('-', '-')) { a = Unary(); if(!a->IsNumber()) ThrowError("invalid type for prefix '--' operator"); if(!a.lvalue) ThrowError("l-value required for prefix '--' operator"); *a.lvalue = a->GetNumber() - 1; } else if(Char('-')) { a = Unary(); if(a->IsNumber()) a = -a->GetNumber(); else ThrowError("incompatible types for unary '-' operator"); } else if(Char('+')) { a = Unary(); if(a->IsNumber()) a = -a->GetNumber(); else ThrowError("incompatible types for unary '+' operator"); } else if(Char('!')) a = !IsTrue(Unary()); else if(Char('~')) { a = Unary(); if(a->IsNumber()) a = ~(int)a->GetNumber(); else ThrowError("incompatible types for unary '~' operator"); } else a = Term(); if(Char2('+', '+')) { if(!a->IsNumber()) ThrowError("invalid type for postfix '++' operator"); if(!a.lvalue) ThrowError("l-value required for postfix '++' operator"); SRVal x = a->GetNumber(); *a.lvalue = a->GetNumber() + 1; return x; } if(Char2('-', '-')) { if(!a->IsNumber()) ThrowError("invalid type for postfix '--' operator"); if(!a.lvalue) ThrowError("l-value required for postfix '--' operator"); SRVal x = a->GetNumber(); *a.lvalue = a->GetNumber() - 1; return x; } return a; } Sic::SRVal Sic::Operator(Sic::SRVal a, Sic::SRVal b, const char *oper, const char *op, bool sym) { if(a->IsMap()) { const ArrayMap& m = a->GetMap(); int q = m.Find(String(oper)); if(q >= 0 && m[q].IsLambda()) { Vector arg; arg.SetCount(1); arg[0] = b; ExecuteLambda(oper, m[q], a.lvalue ? a.lvalue : &a.rvalue, arg); return a; } } if(sym) return Operator(b, a, oper, op); ThrowError(String() << "incompatible types for '" << op << "' operator"); return a; } Sic::SRVal Sic::Mul() { SRVal a = Unary(); for(;;) if(!IsChar2('*', '=') && Char('*')) { SRVal b = Unary(); if(a->IsNumber() && b->IsNumber()) a = a->GetNumber() * b->GetNumber(); else a = Operator(~a, ~b, "operator*=", "*", true); } else if(!IsChar2('/', '=') && Char('/')) { SRVal b = Unary(); if(a->IsNumber() && b->IsNumber()) a = a->GetNumber() / b->GetNumber(); else a = Operator(~a, b, "operator/=", "/"); } else if(!IsChar2('%', '=') && Char('%')) { SRVal b = Unary(); if(a->IsNumber() && b->IsNumber()) a = (int)a->GetNumber() % (int)b->GetNumber(); else a = Operator(~a, b, "operator%=", "%"); } else return a; } Sic::SRVal Sic::Add() { SRVal a = Sic::Mul(); for(;;) if(!IsChar2('+', '=') && Char('+')) { SRVal b = Mul(); if(a->IsNumber() && b->IsNumber()) a = a->GetNumber() + b->GetNumber(); else if(a->IsString() && b->IsString()) a = a->GetString() + b->GetString(); else a = Operator(~a, ~b, "operator+=", "+", true); } else if(!IsChar2('-', '=') && Char('-')) { SRVal b = Mul(); if(a->IsNumber() && b->IsNumber()) a = a->GetNumber() - b->GetNumber(); else a = Operator(~a, b, "operator-=", "-"); } else return a; } Sic::SRVal Sic::Shift() { SRVal a = Add(); for(;;) if(Char2('<', '<')) { SRVal b = Add(); if(a->IsNumber() && b->IsNumber()) a = (int)a->GetNumber() << (int)b->GetNumber(); else { if(a->IsVoid()) if(a.lvalue) *a.lvalue = String(); else a.rvalue = String(); if(a->IsString()) { if(b->IsString()) a.lvalue->GetString() << b->GetString(); else if(b->IsNumber()) a.lvalue->GetString() << b->GetNumber(); else if(!b->IsVoid()) ThrowError("Not implemented"); } else a = Operator(a, b, "operator<<", "<<", true); } } else if(Char2('>', '>')) { SRVal b = Add(); if(a->IsNumber() && b->IsNumber()) a = (int)a->GetNumber() >> (int)b->GetNumber(); else ThrowError("incompatible types for '>>' operator"); } else return a; } int Sic::DoCompare(const SVal& a) { SVal b = Shift(); if(a.IsNumber() && b.IsNumber()) return a.GetNumber() - b.GetNumber(); if(a.IsString() && b.IsString()) return a.GetString().Compare(b.GetString()); ThrowError("incompatible types for comparison operator"); return 0; } Sic::SRVal Sic::Compare() { SRVal a = Shift(); for(;;) if(Char2('>', '=')) a = DoCompare(a) >= 0; else if(Char2('<', '=')) a = DoCompare(a) <= 0; else if(Char('>')) a = DoCompare(a) > 0; else if(Char('<')) a = DoCompare(a) < 0; else return a; } Sic::SRVal Sic::Equal() { SRVal a = Compare(); for(;;) if(Char2('=', '=')) { SRVal b = Compare(); a = int(~a == ~b); } else if(Char2('!', '=')) { SRVal b = Compare(); a = int(~a != ~b); } else return a; } Sic::SRVal Sic::BinAnd() { SRVal a = Equal(); while(!IsChar2('&', '&') && Char('&')) { SRVal b = Equal(); if(a->IsNumber() && b->IsNumber()) a = (int)a->GetNumber() & (int)b->GetNumber(); else ThrowError("incompatible types for '&' operator"); } return a; } Sic::SRVal Sic::BinXor() { SRVal a = BinAnd(); while(Char('^')) { SRVal b = BinAnd(); if(a->IsNumber() && b->IsNumber()) a = (int)a->GetNumber() ^ (int)b->GetNumber(); else ThrowError("incompatible types for '^' operator"); } return a; } Sic::SRVal Sic::BinOr() { SRVal a = BinXor(); while(!IsChar2('|', '|') && Char('|')) { SRVal b = BinXor(); if(a->IsNumber() && b->IsNumber()) a = (int)a->GetNumber() | (int)b->GetNumber(); else ThrowError("incompatible types for '|' operator"); } return a; } Sic::SRVal Sic::And() { SRVal a = BinOr(); while(Char2('&', '&')) { SRVal b = BinOr(); a = IsTrue(a) && IsTrue(b); } return a; } Sic::SRVal Sic::Or() { SRVal a = And(); while(Char2('|', '|')) { SRVal b = And(); a = IsTrue(a) || IsTrue(b); } return a; } Sic::SRVal Sic::Cond() { SRVal a = Or(); if(Char('?')) { SRVal b = Or(); PassChar(':'); SRVal c = Or(); return IsTrue(a) ? b : c; } return a; } Sic::SRVal Sic::Assign() { SRVal a = Cond(); if(Char('=')) { SRVal b = Assign(); if(!a.lvalue) ThrowError("l-value required on the left side of '=' operator"); *a.lvalue = b; } else if(Char2('+', '=')) { SRVal b = Assign(); if(!a.lvalue) ThrowError("l-value required on the left side of '+=' operator"); if(a->IsNumber() && b->IsNumber()) *a.lvalue = a->GetNumber() + b->GetNumber(); else if(a->IsString() && b->IsString()) a.lvalue->GetString() += b->GetString(); else Operator(a, b, "operator+=", "+="); } else if(Char2('-', '=')) { SRVal b = Assign(); if(!a.lvalue) ThrowError("l-value required on the left side of '-=' operator"); if(a->IsNumber() && b->IsNumber()) *a.lvalue = a->GetNumber() - b->GetNumber(); else Operator(a, b, "operator-=", "-="); } else if(Char2('*', '=')) { SRVal b = Assign(); if(!a.lvalue) ThrowError("l-value required on the left side of '*=' operator"); if(a->IsNumber() && b->IsNumber()) *a.lvalue = a->GetNumber() * b->GetNumber(); else Operator(a, b, "operator*=", "*="); } else if(Char2('/', '=')) { SRVal b = Assign(); if(!a.lvalue) ThrowError("l-value required on the left side of '/=' operator"); if(a->IsNumber() && b->IsNumber()) *a.lvalue = a->GetNumber() / b->GetNumber(); else Operator(a, b, "operator/=", "/="); } else if(Char2('%', '=')) { SRVal b = Assign(); if(!a.lvalue) ThrowError("l-value required on the left side of '%=' operator"); if(a->IsNumber() && b->IsNumber()) *a.lvalue = (int)a->GetNumber() % (int)b->GetNumber(); else Operator(a, b, "operator%=", "%="); } else if(Char2(':', '=')) { if(!a.lvalue) ThrowError("l-value required on the left side of ':='"); while(Char('.')) { String id = ReadName(); a.lvalue->Set(id) = ReadLambda(); } } return a; } Sic::SRVal Sic::Exp() { return Assign(); } void Sic::SkipTerm() { if(IsEof()) throw CParser::Error("unexpected end of file"); CParser::SkipTerm(); } void Sic::SkipExp() { int level = 0; for(;;) { if(IsChar(';')) return; if(IsChar(')') && level == 0) return; if(Char(')')) level--; else if(Char('(')) level++; else SkipTerm(); } } void Sic::SkipStatement() { for(;;) { if(Char(';')) return; if(Char('{')) break; SkipTerm(); } int level = 1; while(level > 0) { if(Char('{')) level++; else if(Char('}')) level--; else SkipTerm(); } } bool Sic::PCond() { PassChar('('); bool c = IsTrue(Exp()); PassChar(')'); return c; } void Sic::FinishSwitch() { while(!Char('}') && no_break && no_return) { if(Id("case")) { Exp(); PassChar(':'); } else if(Id("default")) PassChar(':'); DoStatement(); } } void Sic::DoStatement() { if(Id("if")) if(PCond()) { DoStatement(); if(Id("else")) SkipStatement(); } else { SkipStatement(); if(Id("else")) DoStatement(); } else if(Id("do")) { loop++; const char *term = GetPtr(); do { SetPtr(term); DoStatement(); PassId("while"); } while(PCond() && no_break && no_return); PassChar(';'); no_break = true; loop--; } else if(Id("while")) { loop++; const char *term = GetPtr(); for(;;) { SetPtr(term); if(!PCond() || !no_break || !no_return) { SkipStatement(); break; } DoStatement(); } no_break = true; loop--; } else if(Id("for")) { loop++; PassChar('('); if(!Char(';')) { Exp(); PassChar(';'); } const char *cond = NULL; if(!Char(';')) { cond = GetPtr(); SkipExp(); PassChar(';'); } const char *after = NULL; if(!Char(')')) { after = GetPtr(); SkipExp(); PassChar(')'); } const char *stmt = GetPtr(); for(;;) { bool c = true; if(cond) { SetPtr(cond); c = IsTrue(Exp()); } SetPtr(stmt); if(!c || !no_break || !no_return) { SkipStatement(); break; } DoStatement(); if(after) { SetPtr(after); Exp()->Dump(); } } no_break = true; loop--; } else if(Id("foreach")) { loop++; PassChar('('); SVal *value = Exp().lvalue; SVal *key = NULL; if(Char(':')) { key = value; value = Exp().lvalue; } if(!value || !key) ThrowError("l-value expected in 'foreach' statement"); PassId("in"); SVal c = Exp(); if(!c.IsMap()) ThrowError("container expected in 'foreach' statement"); PassChar(')'); const char *stmt = GetPtr(); const ArrayMap& map = c.GetMap(); for(int i = 0; i < map.GetCount() && no_break && no_return; i++) { if(!map.IsUnlinked(i) && !map[i].IsVoid()) { if(key) *key = map.GetKey(i); *value = map[i]; SetPtr(stmt); DoStatement(); } } no_break = true; loop--; } else if(Id("break")) { if(!loop) ThrowError("misplaced 'break'"); no_break = false; PassChar(';'); } else if(Id("case")) ThrowError("misplaced 'case'"); else if(Id("default")) ThrowError("misplaced 'default'"); else if(Id("else")) ThrowError("misplaced 'else'"); else if(Id("return")) { no_return = false; if(!Char(';')) { return_value = Exp(); PassChar(';'); } else return_value = Null; } else if(Id("switch")) { loop++; PassChar('('); SVal a = Exp(); PassChar(')'); PassChar('{'); while(!Char('{')) { if(Id("case")) { SVal b = Exp(); PassChar(':'); if(a == b) { FinishSwitch(); break; } } else if(Id("default")) { PassChar(':'); FinishSwitch(); break; } else SkipStatement(); } loop--; no_break = true; } else if(Char('{')) { while(!Char('}') && no_break && no_return) DoStatement(); } else if(!Char(';')) { SVal v = Exp(); Char(';'); } } void Sic::Run() { no_return = no_break = true; loop = 0; while(!IsEof() && no_return && no_break) DoStatement(); } void Scan(ArrayMap& global, const char *file) { Sic p(global, file); while(!p.IsEof()) { SVal& v = global.GetAdd(p.ReadId()); SLambda& l = v.CreateLambda(); LambdaArgs(p, l); const char *t = p.GetPtr(); if(!p.IsChar('{')) p.ThrowError("missing function body"); p.SkipStatement(); l.code = String(t, p.GetPtr()); } } SVal Execute(ArrayMap& global, const char *name, const Vector& arg) { int ii = global.Find(name); if(ii < 0 || !global[ii].IsLambda()) throw CParser::Error("invalid function name"); const SLambda& l = global[ii].GetLambda(); if(arg.GetCount() != l.arg.GetCount()) throw CParser::Error("invalid number of arguments"); Sic sub(global, l.code); for(int i = 0; i < l.arg.GetCount(); i++) { Sic::Var& v = sub.var.GetAdd(l.arg[i]); v.var = arg[i]; if(l.ref[i]) v.ref = &v.var; } sub.Run(); return sub.return_value; } SVal Execute(ArrayMap& global, const char *name) { return Execute(global, name, Vector()); }