22 Ревизии

Автор SHA1 Съобщение Дата
  MaximeBarniaudy e5a8a5dbf9 Remove old unused code, and measure parsing speed преди 11 месеца
  MaximeBarniaudy c8a1e19b47 Use newest primitives for character class and string matching преди 11 месеца
  MaximeBarniaudy a988c7932a Build Grammar objects whose methods are expression representations of grammar rules преди 11 месеца
  MaximeBarniaudy c3091ad1ca Fix errors after SetVar behavior change and let removal преди 11 месеца
  MaximeBarniaudy 06f1b199f1 Delare Stream object in C code and add primitive for stream string matching. Also refactor the rollback mechanism in the grammar parser to give responsibility to the right object. преди 11 месеца
  MaximeBarniaudy ae3c6c9a9e Make grammar_parser.meta use the entire grammar from minproto.leg преди 11 месеца
  MaximeBarniaudy fe37f0540d Add optional base argument to String.asInteger преди 11 месеца
  MaximeBarniaudy b26a8b7a59 Fix error after single quote semantics change преди 1 година
  MaximeBarniaudy 64397bf953 Add with and without functions for namespace manimulation, add namespace support to grammars, add test of parser circularity преди 1 година
  MaximeBarniaudy a0a7440d85 parser circularity преди 1 година
  MaximeBarniaudy 59b776efb7 dotdot got lost in merging преди 1 година
  MaximeBarniaudy 700023a866 Add Invoke and GetProp преди 1 година
  MaximeBarniaudy 5351152212 Minimal grammar parsing преди 1 година
  MaximeBarniaudy 85147a37b9 Add an intern primitive for symbol creation преди 1 година
  MaximeBarniaudy 0bae1d68ba Fix range access overshadowing key access преди 1 година
  WitherFlower 838e3451cb Merge range access and grammar parser changes преди 1 година
  Ian Piumarta 3d36581570 Include libffi in executable. преди 11 месеца
  Ian Piumarta 4b75732a7e Support super.id() to call method id in the delegate of the object in which the currently running method was found. Add __extern__ to support foreign function calls. преди 11 месеца
  Ian Piumarta f2e790cb26 Add String.bitSet, .bitClear, .bitInvert, bitTest, .charClass, .compareFrom. преди 11 месеца
  Ian Piumarta 1fd5a0be12 New reference output. преди 11 месеца
  Ian Piumarta e57242eb5d Prefix 'local' constrins variables to be in current scope. Variable references, inrement operator l-values, and function definitions can all be qualified 'local' or 'global'. преди 11 месеца
  Ian Piumarta 4622db9d34 Simplify variable lookup and implicit variable declarations. преди 11 месеца
променени са 8 файла, в които са добавени 2276 реда и са изтрити 112 реда
  1. +1
    -1
      Makefile
  2. +1057
    -0
      grammar_parser.meta
  3. +776
    -93
      minproto.leg
  4. +73
    -0
      rawMetaGrammar.leg
  5. +78
    -0
      rawgrammar.leg
  6. +267
    -0
      rawminproto.leg
  7. +3
    -6
      test.ref
  8. +21
    -12
      test.txt

+ 1
- 1
Makefile Целия файл

@ -9,7 +9,7 @@ OFLAGS += $(GFLAGS) -O3 -DNDEBUG -DTYPECODES=1 -DDELOPT=1
PFLAGS += $(OFLAGS) -pg
CFLAGS += -D_GNU_SOURCE -I/opt/local/include
LDLIBS += -L/opt/local/lib
LDLIBS += -lgc -lm
LDLIBS += -lgc -lm -lffi
MAIN = minproto

+ 1057
- 0
grammar_parser.meta
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 776
- 93
minproto.leg
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 73
- 0
rawMetaGrammar.leg Целия файл

@ -0,0 +1,73 @@
statement = b:block { b; }
| e:expression SEMI { e; }
expression = p:primary
( DOT i:id ASSIGN e:expression # { $$ = newSetProp(p, i, e) }
| LBRAK i:expression RBRAK ASSIGN e:expression { SetArray.new(object: p, index: i, value: e); }
)
| i:id ASSIGN e:expression { SetVar.new(name: i, value: e); }
| pf:postfix { pf; }
postfix = p:primary
( DOT i:id a:args !ASSIGN !LBRACE { p = Invoke.new(self: p, method: i, arguments: a); }
| DOT i:id !ASSIGN { p = GetProp.new(object: p, key: i); }
| a:args !ASSIGN !LBRACE { p = Call.new(function: p, arguments: a); }
) * { p; }
args = LPAREN a:mklist
(
( k:id COLON e:expression { a[k] = e; }
| e:expression { a.push(e); }
)
( COMMA
( k:id COLON e:expression { a[k] = e; }
| e:expression { a.push(e); }
)
) *
) ? RPAREN { a; }
mklist = { Object.new(); }
primary = nil | number | var | subExpr
subExpr = LPAREN e:expression RPAREN { e; }
block = LBRACE b:mklist
( e:statement { b.push(e); }
) * RBRACE { Block.new(body: b); }
nil = NIL { nil; }
number = "-" n:unsign { Unyop.new(operation: __opNeg).push(n) }
| "+" n:number { n }
| n:unsign { n }
unsign = < DIGIT+ > - { yytext.asInteger(10); }
var = i:id { GetVar.new(name: i); }
id = < LETTER ALNUM* > - { intern(yytext); }
DIGIT = [0-9]
LETTER = [A-Za-z_]
ALNUM = LETTER | DIGIT
NIL = "nil" !ALNUM -
SEMI = ";" -
COMMA = "," -
LPAREN = "(" -
RPAREN = ")" -
LBRAK = "[" -
RBRAK = "]" -
LBRACE = "{" -
RBRACE = "}" -
COLON = ":" ![:] -
ASSIGN = "=" ![=] -
DOT = "." ![.] -
- = ( space | comment )*
space = ' ' | '\t' | end-of-line
comment = '//' ( !end-of-line . )* end-of-line
end-of-line = '\r\n' | '\n' | '\r'
end-of-file = !.

+ 78
- 0
rawgrammar.leg Целия файл

@ -0,0 +1,78 @@
grammar = -
( d:definition { setInTopNamespace(d.name, d.expression); }
) + end-of-file
definition = i:identifier ASSIGN e:expression SEMI? { Definition.new(name: i, expression: e); }
expression = s:sequence !BAR { s; }
| s1:sequence { s1 = Alternation.new().push(s1); }
( BAR s2:sequence { s1.push(s2); }
) * { s1; }
sequence = p:prefix { p = Sequence.new().push(p); }
( q:prefix { p.push(q); }
) * { p; }
prefix = AND a:action { ParseTimeAction.new(action: a); }
|
( AND s:suffix { And.new(expression: s); }
| NOT s:suffix { Not.new(expression: s); }
| s:suffix { s; }
)
suffix = p:primary
( QUERY { p = Optional.new(expression: p); }
| STAR { p = Star.new(expression: p); }
| PLUS { p = Plus.new(expression: p); }
) ? { p; }
primary = i1:identifier COLON i2:ruleCall !ASSIGN { Assignment.new(name: i1, rule: i2); }
| i:ruleCall !ASSIGN { i; }
| LPAREN e:expression RPAREN { e; }
| l:literal { l; }
| c:class { c; }
| DOT { Dot.new(); }
| a:action { a; }
| BEGIN { Begin.new(); }
| END { End.new(); }
identifier = < [-a-zA-Z_][-a-zA-Z_0-9]* > - { intern(yytext); }
ruleCall = n:identifier CCOLON r:identifier { NamespacedRuleCall.new(namespace: n, name: r); }
| i:identifier { RuleCall.new(name: i); }
literal = ['] < ( !['] char )* > ['] - { StringLiteral.new(string: yytext); }
| ["] < ( !["] char )* > ["] - { StringLiteral.new(string: yytext); }
class = '[' < ( !']' range )* > ']' - { CharacterClass.new(value: yytext); }
range = char '-' char | char
char = '\\' [abefnrtv'"\[\]\\]
| '\\' [0-3][0-7][0-7]
| '\\' [0-7][0-7]?
| !'\\' .
action = m:metaLanguage::block - { Action.new(parseTree: m); }
- = ( space | comment )*
space = ' ' | '\t' | end-of-line
comment = '#' ( !end-of-line . )* end-of-line
end-of-line = '\r\n' | '\n' | '\r'
end-of-file = !.
BAR = '|' -
NOT = '!' -
QUERY = '?' -
BEGIN = '<' -
END = '>' -
SEMI = ";" -
CCOLON = "::" -
LPAREN = "(" -
RPAREN = ")" -
ASSIGN = "=" ![=] -
AND = "&" ![&=] -
PLUS = "+" ![+=] -
STAR = "*" ![=] -
DOT = "." ![.] -
COLON = ":" ![:] -

+ 267
- 0
rawminproto.leg Целия файл

@ -0,0 +1,267 @@
start = - ( s:stmt { s; }
| !. # { yysval = 0 }
| < (!EOL .)* > # { fatal("syntax error near: %s", yytext) }
)
stmt = WHILE LPAREN c:expr RPAREN s:stmt { While.new(condition: c, body: s) }
| IF LPAREN c:expr RPAREN s:stmt
( ELSE t:stmt { If.new(condition: c, consequent: s, alternate: t ) }
| { If.new(condition: c, consequent: s, alternate: nil) }
)
| CONT EOS { Continue.new() }
| BREAK e:expr EOS { Break.new(value: e) }
| BREAK EOS { Break.new(value: nil) }
| RETURN e:expr EOS { Return.new(value: e) }
| RETURN EOS { Return.new(value: nil) }
| FOR LPAREN i:id IN e:expr RPAREN
s:stmt { ForIn.new(identifier: i, expression: e, body: s) }
| FOR LPAREN i:id FROM a:expr
TO b:expr RPAREN s:stmt { ForFromTo.new(identifier: i, first: a, last: b, body: s) }
| FOR LPAREN i:expr SEMI c:expr SEMI
u:expr RPAREN s:stmt { For.new(initialise: i, condition: c, update: u, body: s) }
| TRY t:stmt
( CATCH LPAREN i:id RPAREN c:stmt { TryCatch.new(statement: t, identifier: i, handler: c) }
| ENSURE e:stmt { TryEnsure.new(statement: t, handler: e) }
)
| RAISE e:expr EOS { Raise.new(value: e) }
| LOCAL i:id p:params b:block { SetLocal.new(name: i, value: Lambda.new(parameters: p, body: b)) }
| GLOBAL i:id p:params b:block { SetGlobal.new(name: i, value: Lambda.new(parameters: p, body: b)) }
| i:id p:params b:block { SetVar.new(name: i, value: Lambda.new(parameters: p, body: b)) }
| v:proto DOT i:id p:params b:block { SetProp.new(object: v, key: i, value: Lambda.new(parameters: p, body: b)) }
| b:block { Block.new(body: b) }
| e:expr EOS { e }
proto = v:var ( DOT j:id !LPAREN { v = GetProp.new(object: v, key: j) }
)* { v }
EOS = SEMI+ | &RBRACE | &ELSE | &CATCH
expr = LOCAL i:id ASSIGN e:expr { SetLocal.new(name: i, value: e) }
| GLOBAL i:id ASSIGN e:expr { SetGlobal.new(name: i, value: e) }
| i:id ASSIGN e:expr { SetVar.new(name: i, value: e) }
| l:logor ( ASSIGN r:expr { l = assign(l, r) }
| PLUSEQ r:expr { l = newBinop(__opPreAdd, lvalue(l), r) }
| MINUSEQ r:expr { l = newBinop(__opPreSub, lvalue(l), r) }
| STAREQ r:expr { l = newBinop(__opPreMul, lvalue(l), r) }
| SLASHEQ r:expr { l = newBinop(__opPreDiv, lvalue(l), r) }
| PCENTEQ r:expr { l = newBinop(__opPreMod, lvalue(l), r) }
| SHLEQ r:expr { l = newBinop(__opPreShl, lvalue(l), r) }
| SHREQ r:expr { l = newBinop(__opPreShr, lvalue(l), r) }
| ANDEQ r:expr { l = newBinop(__opPreAnd, lvalue(l), r) }
| XOREQ r:expr { l = newBinop(__opPreXor, lvalue(l), r) }
| OREQ r:expr { l = newBinop(__opPreOr, lvalue(l), r) }
)? { l }
logor = l:logand ( BARBAR r:logand { l = newBinop(__opLogOr, l, r) }
)* { l }
logand = l:bitor ( ANDAND r:bitor { l = newBinop(__opLogAnd, l, r) }
)* { l }
bitor = l:bitxor ( OR r:bitxor { l = newBinop(__opBitOr, l, r) }
)* { l }
bitxor = l:bitand ( XOR r:bitand { l = newBinop(__opBitXor, l, r) }
)* { l }
bitand = l:eq ( AND r:eq { l = newBinop(__opBitAnd, l, r) }
)* { l }
eq = l:ineq ( EQ r:ineq { l = newBinop(__opEq, l, r) }
| NOTEQ r:ineq { l = newBinop(__opNotEq, l, r) }
)* { l }
ineq = l:shift ( LESS r:shift { l = newBinop(__opLess, l, r) }
| LESSEQ r:shift { l = newBinop(__opLessEq, l, r) }
| GRTREQ r:shift { l = newBinop(__opGrtrEq, l, r) }
| GRTR r:shift { l = newBinop(__opGrtr, l, r) }
)* { l }
shift = l:sum ( SHL r:sum { l = newBinop(__opShl, l, r) }
| SHR r:sum { l = newBinop(__opShr, l, r) }
)* { l }
sum = l:prod ( PLUS r:prod { l = newBinop(__opAdd, l, r) }
| MINUS r:prod { l = newBinop(__opSub, l, r) }
)* { l }
prod = l:range ( STAR r:range { l = newBinop(__opMul, l, r) }
| SLASH r:range { l = newBinop(__opDiv, l, r) }
| PCENT r:range { l = newBinop(__opMod, l, r) }
) * { l }
range = i1:prefix ( DOTDOT i2:prefix { i1 = Range.new(start: i1, end: i2) }
) ? { i1 }
prefix = PPLUS p:prefix { newBinop(__opPreAdd, lvalue(p), 1) }
| MMINUS p:prefix { newBinop(__opPreSub, lvalue(p), 1) }
| PLING p:prefix { Unyop.new(operation: __opNot).push(p) }
| MINUS p:prefix { Unyop.new(operation: __opNeg).push(p) }
| TILDE p:prefix { Unyop.new(operation: __opCom).push(p) }
| BQUOTE s:stmt { Unyop.new(operation: __opQuasiquote).push(s) }
| COMMAT e:expr { Unyop.new(operation: __opUnquote).push(e) }
| postfix
postfix = SUPER DOT i:id a:args { Super.new(method: i, arguments: a) }
| p:primary
( LBRAK e:expr RBRAK { p = GetArray.new(object: p, index: e) }
| DOT i:id ( a:args !LBRACE { p = Invoke.new(self: p, method: i, arguments: a) }
| { p = GetProp.new(object: p, key: i) }
)
| a:args !LBRACE { p = newApply(p, a) }
)*
( PPLUS { p = newBinop(__opPostAdd, lvalue(p), 1) }
| MMINUS { p = newBinop(__opPostAdd, lvalue(p), -1) }
)? { p }
args = LPAREN a:mkobj
( RPAREN
| ( k:id COLON e:expr { a[k] = e }
| e:expr { a.push(e) }
)
( COMMA ( k:id COLON e:expr { a[k] = e }
| e:expr { a.push(e) }
) )* RPAREN ) { a }
params = LPAREN p:mkobj
( RPAREN
| i:id ( COLON e:expr { p[i] = e }
| { p.push(i) }
)
( COMMA i:id ( COLON e:expr { p[i] = e }
| { p.push(i) }
)
)* RPAREN ) { p }
mkobj = { Object.new() }
primary = nil | number | string | symbol | var | lambda | subexpr | literal
lambda = p:params b:block { Lambda.new(parameters: p, body: b) }
subexpr = LPAREN e:expr RPAREN { e }
| b:block { Block.new(body: b) }
literal = LBRAK o:mkobj
( RBRAK
| ( ( i:id COLON e:expr { o[i] = e }
| e:expr { o.push(e) }
) ( COMMA ( i:id COLON e:expr { o[i] = e }
| e:expr { o.push(e) }
) )* )? RBRAK ) { Literal.new(object: o) }
block = LBRACE b:mkobj
( e:stmt { b.push(e) }
)* RBRACE { Block.new(body: b) }
nil = NIL { nil }
number = "-" n:unsign { Unyop.new(operation: __opNeg).push(n) }
| "+" n:number { n }
| n:unsign { n }
unsign = < DIGIT* '.' DIGIT+ EXP? > - { yytext.asFloat() }
| "0" [bB] < BIGIT+ > - { yytext.asInteger(2) }
| "0" [xX] < HIGIT+ > - { yytext.asInteger(16) }
| "0" < OIGIT* > - { yytext.asInteger(8) }
| < DIGIT+ > - { yytext.asInteger() }
| "'" < char > "'" - { ord(yytext.unescaped()) }
string = '"' < ( !'"' char )* > '"' - { yytext.unescaped() }
char = "\\" ( ["'\\abfnrtv]
| [xX] HIGIT*
| [0-7][0-7]?[0-7]?
)
| .
symbol = HASH i:id { i }
var = LOCAL i:id { GetLocal.new(name: i) }
| GLOBAL i:id { GetGlobal.new(name: i) }
| i:id { GetVar.new(name: i) }
id = < LETTER ALNUM* > - { intern(yytext) }
BIGIT = [0-1]
OIGIT = [0-7]
DIGIT = [0-9]
HIGIT = [0-9A-Fa-f]
LETTER = [A-Za-z_]
ALNUM = LETTER | DIGIT
SIGN = [-+]
EXP = [eE] SIGN DIGIT+
- = SPACE*
SPACE = [ \t] | EOL | SLC | MLC
EOL = [\n\r] # { ++lineno }
SLC = "//" (!EOL .)*
MLC = "/*" ( MLC | !"*/" (EOL | .))* "*/" -
NIL = "nil" !ALNUM -
WHILE = "while" !ALNUM -
IF = "if" !ALNUM -
ELSE = "else" !ALNUM -
FOR = "for" !ALNUM -
IN = "in" !ALNUM -
FROM = "from" !ALNUM -
TO = "to" !ALNUM -
CONT = "continue" !ALNUM -
BREAK = "break" !ALNUM -
RETURN = "return" !ALNUM -
TRY = "try" !ALNUM -
CATCH = "catch" !ALNUM -
ENSURE = "ensure" !ALNUM -
RAISE = "raise" !ALNUM -
GLOBAL = "global" !ALNUM -
LOCAL = "local" !ALNUM -
SUPER = "super" !ALNUM -
BQUOTE = "`" -
COMMAT = "@" -
HASH = "#" -
SEMI = ";" -
ASSIGN = "=" ![=] -
COMMA = "," -
COLON = ":" ![:] -
LPAREN = "(" -
RPAREN = ")" -
LBRAK = "[" -
RBRAK = "]" -
LBRACE = "{" -
RBRACE = "}" -
BARBAR = "||" ![=] -
ANDAND = "&&" ![=] -
OR = "|" ![|=] -
OREQ = "|=" -
XOR = "^" ![=] -
XOREQ = "^=" -
AND = "&" ![&=] -
ANDEQ = "&=" -
EQ = "==" -
NOTEQ = "!=" -
LESS = "<" ![<=] -
LESSEQ = "<=" -
GRTREQ = ">=" -
GRTR = ">" ![=] -
SHL = "<<" ![=] -
SHLEQ = "<<=" -
SHR = ">>" ![=] -
SHREQ = ">>=" -
PLUS = "+" ![+=] -
PLUSEQ = "+=" -
PPLUS = "++" -
MINUS = "-" ![-=] -
MINUSEQ = "-=" -
MMINUS = "--" -
STAR = "*" ![=] -
STAREQ = "*=" -
SLASH = "/" ![/=] -
SLASHEQ = "/=" -
PCENT = "%" ![=] -
PCENTEQ = "%=" -
DOT = "." ![.] -
DOTDOT = ".." -
PLING = "!" ![=] -
TILDE = "~" -

+ 3
- 6
test.ref Целия файл

@ -147,13 +147,10 @@ f();
f 102 ( 40 ) 41 32 { 123 32 p 112 r 114 i 105 n 110 t 116 ( 40 _ 95 _ 95 e 101 n 110 v 118 _ 95 _ 95 ( 40 ) 41 ) 41 32 } 125
10 f 102 ( 40 ) 41 ; 59
10
<<Binop>>
| operation: 15
| 0: 6
| 1: 7
42
42
6 * 7
42
42
MACRO table <<Object>>
| assert: <<Closure>>
| | environment: nil
@ -261,4 +258,4 @@ AST eval => 42
3: if (n < 2) nil else n * factorial(n - 1)
2: factorial(5)
1: { factorial(5) }
0: try { factorial(5) } catch (e) { { if (!eval(e.__function__ == "*")) { print("\nassertion failed: ", "e.__function__ == \"*\"", "\n"); exit(1) } }; { if (!eval(e.__kind__ == "type error")) { print("\nassertion failed: ", "e.__kind__ == \"type error\"", "\n"); exit(1) } }; { if (!eval(e.__message__ == "illegal operand types")) { print("\nassertion failed: ", "e.__message__ == \"illegal operand types\"", "\n"); exit(1) } }; { if (!eval(e.operand1 == 2)) { print("\nassertion failed: ", "e.operand1 == 2", "\n"); exit(1) } }; { if (!eval(e.operand2 == nil)) { print("\nassertion failed: ", "e.operand2 == nil", "\n"); exit(1) } }; print(e.__function__, ": ", e.__kind__, ": ", e.__message__, ": ", typeName(e.operand1), " and ", typeName(e.operand2), "\n"); let w = 2; let j = i; while (j /= 10 >= 0) { w += 1 }; for (i from len(e) - 1 to 0) print(pad(w, codeString(i)), ": ", codeString(e[i]), "\n") }
0: try { factorial(5) } catch (e) { { if (!eval(e.__function__ == "*")) { print("\nassertion failed: ", "e.__function__ == \"*\"", "\n"); exit(1) } }; { if (!eval(e.__kind__ == "type error")) { print("\nassertion failed: ", "e.__kind__ == \"type error\"", "\n"); exit(1) } }; { if (!eval(e.__message__ == "illegal operand types")) { print("\nassertion failed: ", "e.__message__ == \"illegal operand types\"", "\n"); exit(1) } }; { if (!eval(e.operand1 == 2)) { print("\nassertion failed: ", "e.operand1 == 2", "\n"); exit(1) } }; { if (!eval(e.operand2 == nil)) { print("\nassertion failed: ", "e.operand2 == nil", "\n"); exit(1) } }; print(e.__function__, ": ", e.__kind__, ": ", e.__message__, ": ", typeName(e.operand1), " and ", typeName(e.operand2), "\n"); backtrace(e) }

+ 21
- 12
test.txt Целия файл

@ -152,8 +152,8 @@ assert((0b1110 & 0b0111) == 0b0110);
nt = 0;
nf = 0;
t() { nt = nt + 1; #t }
f() { nf = nf + 1; nil }
t() { global nt += 1; #t }
f() { global nf += 1; nil }
refute(f() || f()); assert(nt == 0); assert(nf == 2);
assert(f() || t()); assert(nt == 1); assert(nf == 3);
@ -378,6 +378,12 @@ ast = (`6 * @myNode;); // user-defined node in AST
print("AST eval => ", eval(ast), "\n");
print("String to int conversion\n");
print("11111111".asInteger(2), "\n");
print("377".asInteger(8), "\n");
print("255".asInteger(), "\n");
print("FF".asInteger(16), "\n");
for (i from 0 to 10) print(i, " "); print("\n");
for (i from 10 to 0) print(i, " "); print("\n");
for (i in 10) print(i, " "); print("\n");
@ -388,16 +394,23 @@ factorial(n) { if (n < 2) nil else n * factorial(n-1) }
typeName(x) {
if (!x) return "<Undefined>";
let name = "?";
let level = 1;
name = "?";
level = 1;
while (x && !x.allKeys().includes(#__name__)) x = x.__delegate__;
if (x.allKeys().includes(#__name__)) name = x.__name__.asString();
"<" * level + name + ">" * level;
}
pad(w, i) {
while (len(i) < w) i = " " + i;
i;
backtrace(e) {
pad(w, i) {
while (len(i) < w) i = " " + i;
i;
}
w = 2;
j = len(e);
while ((j /= 10) > 0) { ++w; } // w = 1+log10(j)
for (i from len(e) - 1 to 0)
print(pad(w, codeString(i)), ": ", codeString(e[i]), "\n");
}
try {
@ -410,9 +423,5 @@ catch (e) {
assert(e.operand1 == 2);
assert(e.operand2 == nil);
print(e.__function__, ": ", e.__kind__, ": ", e.__message__, ": ", typeName(e.operand1), " and ", typeName(e.operand2), "\n");
let w = 2;
let j = i;
while ( (j /= 10) > 0) { ++w; }
for (i from len(e) - 1 to 0)
print(pad(w, codeString(i)), ": ", codeString(e[i]), "\n");
backtrace(e);
}

Зареждане…
Отказ
Запис