diff options
| author | Drahflow <drahflow@gmx.de> | 2013-01-03 22:11:37 +0100 |
|---|---|---|
| committer | Drahflow <drahflow@gmx.de> | 2013-01-03 22:11:37 +0100 |
| commit | 19d573cf7c6dd729289ef5151f15db51bcc79d91 (patch) | |
| tree | 288980504ab3721cfa7fd0aea59514957c17b45d | |
| parent | d7d307dc6563fe27bf43cab1e83ac21204fea6c6 (diff) | |
Compiler can now push ints and strings
| -rw-r--r-- | ACME-Bare-Metal/Metal.xs | 10 | ||||
| -rw-r--r-- | ACME-Bare-Metal/t/ACME-Bare-Metal.t | 11 | ||||
| -rw-r--r-- | compiler/elymasAsm.ey | 277 | ||||
| -rw-r--r-- | compiler/elymasAsmLib.ey | 81 | ||||
| -rw-r--r-- | compiler/elymasGlobal.ey | 93 | ||||
| -rw-r--r-- | compiler/standard.ey | 14 | ||||
| -rw-r--r-- | examples/working/bor.ey | 1 | ||||
| -rw-r--r-- | examples/working/int.ey | 6 | ||||
| -rw-r--r-- | examples/working/reverse.ey | 5 | ||||
| -rw-r--r-- | examples/working/string.ey | 1 | ||||
| -rw-r--r-- | interpreter/Elymas.pm | 3 | ||||
| -rw-r--r-- | interpreter/ElymasAsm.pm | 41 | ||||
| -rw-r--r-- | interpreter/ElymasGlobal.pm | 71 | ||||
| -rw-r--r-- | interpreter/ElymasSys.pm | 2 | ||||
| -rw-r--r-- | interpreter/Makefile | 8 | ||||
| -rw-r--r-- | notes | 50 |
16 files changed, 598 insertions, 76 deletions
diff --git a/ACME-Bare-Metal/Metal.xs b/ACME-Bare-Metal/Metal.xs index cb7e5a0..a4e16d3 100644 --- a/ACME-Bare-Metal/Metal.xs +++ b/ACME-Bare-Metal/Metal.xs @@ -17,6 +17,16 @@ allocate(length) OUTPUT: RETVAL +void * +allocateAt(length, addr) + int length + void *addr + CODE: + RETVAL = mmap(addr, length, PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + OUTPUT: + RETVAL + void deallocate(block, length) void *block diff --git a/ACME-Bare-Metal/t/ACME-Bare-Metal.t b/ACME-Bare-Metal/t/ACME-Bare-Metal.t index 0975996..e7e5c26 100644 --- a/ACME-Bare-Metal/t/ACME-Bare-Metal.t +++ b/ACME-Bare-Metal/t/ACME-Bare-Metal.t @@ -8,7 +8,7 @@ use strict; use warnings; -use Test::More tests => 3; +use Test::More tests => 5; BEGIN { use_ok('ACME::Bare::Metal') }; ######################### @@ -24,3 +24,12 @@ ok(ACME::Bare::Metal::peek($block) == 0xC3); ACME::Bare::Metal::execute($block); ACME::Bare::Metal::deallocate($block, 4096); + +$block = ACME::Bare::Metal::allocateAt(4096, 0x0000700000000000); +ok($block > 0, "Block allocation"); + +ACME::Bare::Metal::poke($block, 0xC3); +ok(ACME::Bare::Metal::peek($block) == 0xC3); + +ACME::Bare::Metal::execute($block); +ACME::Bare::Metal::deallocate($block, 4096); diff --git a/compiler/elymasAsm.ey b/compiler/elymasAsm.ey index f62f979..f11f950 100644 --- a/compiler/elymasAsm.ey +++ b/compiler/elymasAsm.ey @@ -3,13 +3,6 @@ 16777216 ==STACKSIZE 128 ==STACKSTART - STACKSIZE sys .asm .alloc ==mainStack - - # global stack layout - # 0 - STACKSTART : global variables - # 0 : current stack pointer - # STACKSTART - ...: real stack - # hex decoding { "(.)(.)" regex { } { "not a valid hex-string" die } ? * @@ -25,14 +18,14 @@ { { streq any }_ ==reg [ - [ /al /ax /eax /rax /none ] - [ /cl /cx /ecx /rcx ] - [ /dl /dx /edx /rdx ] - [ /bl /bx /ebx /rbx ] - [ /spl /ah /sp /esp /rsp ] - [ /bpl /ch /bp /ebp /rbp ] - [ /sil /si /esi /rsi ] - [ /dil /di /edi /rdi ] + [ /zero /al /ax /eax /rax /none ] + [ /one /cl /cx /ecx /rcx ] + [ /two /dl /dx /edx /rdx ] + [ /three /bl /bx /ebx /rbx ] + [ /four /spl /ah /sp /esp /rsp ] + [ /five /bpl /ch /bp /ebp /rbp ] + [ /six /sil /si /esi /rsi ] + [ /seven /dil /di /edi /rdi ] [ /r8b /r8w /r8d /r8 ] [ /r9b /r9w /r9d /r9 ] [ /r10b /r10w /r10d /r10 ] @@ -62,26 +55,89 @@ reg regno 8 mul %38 band add } /modrm00 deff - { 8 { _ 256 mod -01 256 div } rep -- } /imm64 deff - { 4 { _ 256 mod -01 256 div } rep -- } /imm32 deff - { 2 { _ 256 mod -01 256 div } rep -- } /imm16 deff - { } /imm8 deff + { =mem =reg + %C0 + mem regno %07 band add + reg regno 8 mul %38 band add + } /modrm11 deff + + { _ 0 ge assert 8 { _ 256 mod -01 256 div } rep -- } /imm64 deff + { _ 0 ge assert 4 { _ 256 mod -01 256 div } rep -- } /imm32 deff + { _ 0 ge assert 2 { _ 256 mod -01 256 div } rep -- } /imm16 deff + { _ 0 ge assert 255 band } /imm8 deff # instructions { ==reg ==i reg bit64 assert + i 256 lt assert + + 1 /none /none reg rex + %83 + /zero reg modrm11 + i imm8 + } /addqImm8Reg deff + + { ==dst ==src + dst bit64 assert + src bit64 assert + + 1 src /none dst rex + %01 + src dst modrm11 + } /addqRegReg deff + + { ==reg + reg bit64 assert + + reg regno %07 gt { 1 /none /none reg rex } rep + %FF + /two reg modrm11 + } /callqReg deff + + { ==dst ==src + dst bit64 assert + src bit64 assert + + 1 src /none dst rex + %39 + src dst modrm11 + } /cmpqRegReg deff + + { ==offset + offset 128 lt assert + + %76 + offset imm8 + } /jbeRel8 deff + + { ==offset + offset 128 lt assert + + %7E + offset imm8 + } /jleRel8 deff + + { ==reg ==i + reg bit64 assert 1 /none /none reg rex %B8 reg regno %07 band add i imm64 } /movqImmReg deff + { ==reg + reg bit64 assert + + 1 /none /none reg rex + %B8 reg regno %07 band add + } /movqImmOOBReg deff + { ==reg ==mem reg bit64 assert mem bit64 assert 1 reg /none mem rex - %89 + %8B reg mem modrm00 } /movqMemReg deff @@ -90,42 +146,195 @@ mem bit64 assert 1 reg /none mem rex - %8B + %89 reg mem modrm00 } /movqRegMem deff - { =reg + { ==dst ==src + src bit64 assert + dst bit64 assert + + 1 src /none dst rex + %89 + src dst modrm11 + } /movqRegReg deff + + { ==mem ==i + mem bit64 assert + i 256 lt assert + + %80 + /one mem modrm00 + i imm8 + } /orbImmMem64 deff + + { ==reg reg regno %07 gt { 1 /none /none reg rex } rep %58 reg regno %07 band add - } /popq deff + } /popqReg deff - { =reg + { ==reg reg regno %07 gt { 1 /none /none reg rex } rep %50 reg regno %07 band add - } /pushq deff + } /pushqReg deff + + { ==imm + %68 + imm imm32 + } /pushqImm32 deff { %C3 } /retn deff + { ==dst ==src + dst bit64 assert + src bit64 assert + + 1 src /none dst rex + %29 + src dst modrm11 + } /subqRegReg deff + + { + %0F %05 + } /syscall deff + + { ==reg ==mem + reg bit64 assert + mem bit64 assert + + 1 reg /none mem rex + %33 + reg mem modrm00 + } /xorqMemReg deff + + { ==mem ==reg + reg bit64 assert + mem bit64 assert + + 1 reg /none mem rex + %31 + reg mem modrm00 + } /xorqRegMem deff + + { ==dst ==src + dst bit64 assert + src bit64 assert + + 1 src /none dst rex + %31 + src dst modrm11 + } /xorqRegReg deff + + # data manipulation functions + { # ==addr + [ -01 8 { _ sys .asm .peek -01 1 add } rep -- ] reverse { -01 256 mul add } fold + } /peekImm64 deff + + { # ==addr + [ -01 4 { _ sys .asm .peek -01 1 add } rep -- ] reverse { -01 256 mul add } fold + } /peekImm32 deff + + # global stack layout + # 0 - STACKSTART : global variables + # %0 : current stack pointer + # %8 : current scope + # %10 : currently quoted + # STACKSTART - ...: real stack + STACKSIZE sys .asm .alloc ==mainStack + < + mainStack .base ==i + [ + mainStack .base STACKSIZE add imm64 + ] { i sys .asm .poke i 1 add =i } each + > -- + + { + [ -01 16 { _ 16 mod -01 16 div } rep -- ] + [ /0 /1 /2 /3 /4 /5 /6 /7 /8 /9 /A /B /C /D /E /F ] * + reverse |cat fold + } /base16encode64 deff + + { + [ -01 8 { _ 16 mod -01 16 div } rep -- ] + [ /0 /1 /2 /3 /4 /5 /6 /7 /8 /9 /A /B /C /D /E /F ] * + reverse |cat fold + } /base16encode32 deff + + < + [ + 32 { "." } rep + " " "!" "\"" "#" "$" "%" "&" "ยด" "(" ")" "*" "+" "," "-" "." "/" + /0 /1 /2 /3 /4 /5 /6 /7 /8 /9 ":" ";" "<" "=" ">" "?" + "@" /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P + /Q /R /S /T /U /V /W /X /Y /Z "[" "\\" "]" "^" "_" + "`" /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p + /q /r /s /t /u /v /w /x /y /z "{" "|" "}" "~" "." + 128 { "." } rep + ] /ASCII defv + + { + mainStack .base STACKSIZE add ==stackEnd + "Stack ------------\n" sys .out .writestr + mainStack .base peekImm64 ==addr + addr stackEnd gt { [ -01 stackEnd ] die } rep # Stack corrupted + + { addr stackEnd lt } { + [ + addr base16encode64 ": " + addr peekImm64 ==value + value base16encode64 + value 105553116266496 ge value 123145302310912 lt and { + value peekImm32 + value 4 add peekImm32 16777215 band 4294967296 mul + add 8 div ==length + 0 length 1 sub range { + 8 mul value add ==heapAddr + "\n " + heapAddr base16encode64 + ": " + # the perl interpreter does not like full 64bit numbers and converts them into floats + heapAddr 4 add peekImm32 _ ==heapValueB base16encode32 + heapAddr peekImm32 _ ==heapValueA base16encode32 + " " + [ [ heapValueA heapValueB ] { 4 { _ 256 mod -01 256 div } rep -- } each ] ASCII * 8 dearray + } each + } rep + "\n" + ] |cat fold sys .out .writestr + addr 8 add =addr + } loop + "^^^^^^^^^^^^^^^^^^\n" sys .out .writestr + } + > -- /stackDump deff + + { ==opcodes + opcodes len 1 sub PAGESIZE div 1 add PAGESIZE mul sys .asm .alloc /codearea defv + codearea .base ==i + opcodes { i sys .asm .poke i 1 add =i } each + codearea + } /arrayToCode deff + # take an array of instruction bytes and execute it on the given stack { ==stack ==opcodes [ - /rbx pushq + /rbx pushqReg stack /rbx movqImmReg - /rbx /rsp movqMemReg + /rbx /rsp xorqMemReg + /rsp /rbx xorqRegMem + /rbx /rsp xorqMemReg ] opcodes [ stack /rbx movqImmReg - /rsp /rbx movqRegMem - /rbx popq + /rbx /rsp xorqMemReg + /rsp /rbx xorqRegMem + /rbx /rsp xorqMemReg + /rbx popqReg retn ] cat cat =opcodes - opcodes len 1 sub PAGESIZE div 1 add PAGESIZE mul sys .asm .alloc /codearea defv - codearea .base ==i - opcodes { i sys .asm .poke i 1 add =i } each - codearea .base sys .asm .execute - codearea .free + opcodes arrayToCode _ .base sys .asm .execute + .free } /executeOn deff { mainStack .base executeOn } /execute deff diff --git a/compiler/elymasAsmLib.ey b/compiler/elymasAsmLib.ey new file mode 100644 index 0000000..341c07a --- /dev/null +++ b/compiler/elymasAsmLib.ey @@ -0,0 +1,81 @@ +"elymasAsm.ey" include + +< + { assembler -01 . } ":" deff + "%" _ : -01 deff + + < + 1 ==PROT_READ + 2 ==PROT_WRITE + 4 ==PROT_EXEC + + 2 ==MAP_PRIVATE + 16 ==MAP_FIXED + 32 ==MAP_ANONYMOUS + > ==MMAP + + < + 9 ==mmap + > ==SYSCALL + + { =*def ==struct + struct values |cat fold :arrayToCode .base ==address + struct keys { address -101 def struct -01 . len address add =address } each + } /allocateOffsetStruct deff + + # global data + < + # current end of heap memory (grows upwards) + [ %00 %00 %00 %00 %00 %60 %00 %00 ] ==heapEnd + + # next free byte at end of heap + [ %00 %00 %00 %00 %00 %60 %00 %00 ] ==unusedHeapStart + > { defv }' allocateOffsetStruct + + # internal functions, ABI follows SysV standards + + < + # allocate a chunk of memory + # rdi -> size of chunk in bytes + # rax <- address of allocated chunk + # chunk will have GC length header initialized correctly + # FIXME put a real allocator here + [ + /rbx :pushqReg + /rdi :pushqReg + unusedHeapStart /rax :movqImmReg + /rax /rcx :movqMemReg + /rcx /rbx :movqRegReg + /rcx /rdx :movqRegReg + /rdi /rcx :addqRegReg + /rcx /rax :movqRegMem + heapEnd /rax :movqImmReg + /rax /rdi :movqMemReg + /rdi /rcx :cmpqRegReg + [ + 4096 4096 mul /rsi :movqImmReg + /rsi /rdi :addqRegReg + /rdi /rax :movqRegMem + /rsi /rdi :subqRegReg + + SYSCALL .mmap /rax :movqImmReg + # /rdi already fine + # /rsi already fine + < { MMAP -01 . } "!" deff + !PROT_READ !PROT_WRITE !PROT_EXEC bor bor /rdx :movqImmReg + !MAP_PRIVATE !MAP_FIXED !MAP_ANONYMOUS bor bor /r10 :movqImmReg + > -- + /r8 :movqImmOOBReg %FF %FF %FF %FF %FF %FF %FF %FF + 0 /r9 :movqImmReg + :syscall + ] |len { :jbeRel8 } -21*0*221* dearray + /rbx /rax :movqRegReg + /rdi :popqReg + /rdi /rax :movqRegMem + /rbx :popqReg + :retn + ] /internalAllocate defv + > { defv }' allocateOffsetStruct +> /assemblerLibrary defv + +# vim: syn=elymas diff --git a/compiler/elymasGlobal.ey b/compiler/elymasGlobal.ey index c339ede..6a92f50 100644 --- a/compiler/elymasGlobal.ey +++ b/compiler/elymasGlobal.ey @@ -1,13 +1,89 @@ -"elymasAsm.ey" include +"elymasAsmLib.ey" include -0 /TOKINT defv -1 /TOKSTR defv -2 /TOKID defv +< + [ /0 /1 /2 /3 /4 /5 /6 /7 /8 /9 ] ==digits + + { 0 ==result + { "(.)(.*)" regex } { + { streq }_ digits -01 index result 10 mul add =result + } loop + result + } +> -- /base10decode deff + +< + { assembler -01 . } ":" deff + { assemblerLibrary -01 . } "::" deff + "%" _ : -01 deff + + { .value base10decode ==v + [ + # allocate int + 16 /rdi :movqImmReg + ::internalAllocate /rax :movqImmReg + /rax :callqReg + + # push int address on program stack + /rax :pushqReg + + # type zero does not need to be changed + + # load value + 8 /rax :addqImm8Reg + v /rdx :movqImmReg + /rdx /rax :movqRegMem + ] :execute + } /TOKINT + + { .value strToUTF8Bytes _ =*v len _ ==exactLength + 1 sub 8 div 4 add 8 mul ==memoryLength + + memoryLength 2147483648 lt assert + + [ + # allocate string + memoryLength /rdi :movqImmReg + ::internalAllocate /rax :movqImmReg + /rax :callqReg + + # push string address on program stack + /rax :pushqReg + + # set type + 7 /rax :addqImm8Reg + %10 /rax :orbImmMem64 + + # clear hash value + 1 /rax :addqImm8Reg + /rdx /rdx :xorqRegReg + /rdx /rax :movqRegMem + + # load exact length + 8 /rax :addqImm8Reg + exactLength /rdx :movqImmReg + /rdx /rax :movqRegMem + + # load string contents + 0 exactLength 8 div range { 8 mul ==i + 8 /rax :addqImm8Reg + /rdx :movqImmOOBReg i _ 7 add range v 8 dearray + /rdx /rax :movqRegMem + } each + ] :execute + } /TOKSTR -1010 deff + + { ==token + # string value + token TOKSTR + # scope resolution + # FIXME + } /TOKID +> -- 3 |defv rep { /f deff -101 /s defv regex { f } { s } ? * } /rxparse deff { " " cat - { < /type defv /value defv > } /token deff + { < /handle deff /value defv > } /token deff [ -01 { _ "" streq not } { 0 /matched defv { /f deff matched { -- } { { 1 =matched f } rxparse } ? * } /parse deff @@ -34,17 +110,12 @@ } loop -- ] } /tokenize deff -{ - dump - [ ] assembler .execute -} /interpretToken deff - { /input defv "" { 4096 input .readstr cat _ "" streq not } { - { _ "([^\\n]*)\\n(.*)" regex } { -102 -- tokenize |interpretToken each } loop + { _ "([^\\n]*)\\n(.*)" regex } { -102 -- tokenize { _ .handle assembler .stackDump } each } loop } loop } /executeFile deff diff --git a/compiler/standard.ey b/compiler/standard.ey index 0d96431..7df61f3 100644 --- a/compiler/standard.ey +++ b/compiler/standard.ey @@ -7,14 +7,18 @@ } quoted { } { * } ? * } "}_" defq -{ ==f _ ==a len ==l +{ =*f _ =*a len ==l l { - 0 a * - 1 l 1 sub range { a * f * } each + 0 a + 1 l 1 sub range { a f } each } { "fold on empty array" die } ? * } /fold deff +{ _ =*a len ==l + [ 1 l range { l -01 sub a } each ] +} /reverse deff + { |or fold } /any deff { |and fold } /all deff @@ -41,4 +45,8 @@ not { "Assertion failure" die } rep } /assert deff +{ ==s + [ s keys { s -01 . } each ] +} /values deff + # vim: syn=elymas diff --git a/examples/working/bor.ey b/examples/working/bor.ey new file mode 100644 index 0000000..71386df --- /dev/null +++ b/examples/working/bor.ey @@ -0,0 +1 @@ +16 2 bor dump diff --git a/examples/working/int.ey b/examples/working/int.ey new file mode 100644 index 0000000..1f595ec --- /dev/null +++ b/examples/working/int.ey @@ -0,0 +1,6 @@ +0 dump +1 dump +2 dump +1 1 add dump +1 2 add dump +2 1 add dump diff --git a/examples/working/reverse.ey b/examples/working/reverse.ey new file mode 100644 index 0000000..32d0b24 --- /dev/null +++ b/examples/working/reverse.ey @@ -0,0 +1,5 @@ +{ _ /a deff len /l defv + [ 1 l range { l -01 sub a } each ] +} /reverse deff + +[ 3 2 1 ] reverse dump diff --git a/examples/working/string.ey b/examples/working/string.ey index 1174e14..54d1838 100644 --- a/examples/working/string.ey +++ b/examples/working/string.ey @@ -2,3 +2,4 @@ "a\nb" dump "a\\b" dump "a\"b" dump +"thisisalongstring" dump diff --git a/interpreter/Elymas.pm b/interpreter/Elymas.pm index 6bfe4cf..0749443 100644 --- a/interpreter/Elymas.pm +++ b/interpreter/Elymas.pm @@ -52,7 +52,7 @@ sub arrayAccess { my $i = pop @$data or die "Stack underflow"; die "array index must be int" unless $i->[1] eq 'int'; - push @$data, $array->[0]->[$i->[0]]; + push @$data, $array->[0]->[$i->[0] % @{$array->[0]}]; } sub interpretCode { @@ -109,6 +109,7 @@ sub typeEqual { if($a->[0] eq 'range') { return $a->[1] == $b->[1] && $a->[2] == $b->[2]; } elsif($a->[0] eq 'array' or $a->[0] eq 'func') { + return 0 if(not defined $a->[2] or not defined $b->[2]); return 0 if(@{$a->[2]} != @{$b->[2]}); return 0 if(@{$a->[3]} != @{$b->[3]}); diff --git a/interpreter/ElymasAsm.pm b/interpreter/ElymasAsm.pm index 63b7803..2bf2295 100644 --- a/interpreter/ElymasAsm.pm +++ b/interpreter/ElymasAsm.pm @@ -6,25 +6,40 @@ use warnings; use Elymas; use ACME::Bare::Metal; +sub constructBlock { + my ($block, $size) = @_; + + my $scope; $scope = \{ + 'base' => [$block, 'int', 'passive'], + 'size' => [$size, 'int', 'passive'], + 'free' => [sub { + my ($data) = @_; + + ACME::Bare::Metal::deallocate($$scope->{'base'}->[0], $$scope->{'size'}->[0]); + }, ['func', 'sys .asm .free'], 'active'], + }; + + return $$scope; +} + our $asm = { 'alloc' => [sub { - my ($data) = @_; + my ($data) = @_; - my $size = popInt($data); - my $block = ACME::Bare::Metal::allocate($size); + my $size = popInt($data); + my $block = ACME::Bare::Metal::allocate($size); - my $scope; $scope = \{ - 'base' => [$block, 'int', 'passive'], - 'size' => [$size, 'int', 'passive'], - 'free' => [sub { - my ($data) = @_; + push @$data, [enstruct(constructBlock($block, $size))]; + }, ['func', 'sys .asm .alloc'], 'active'], + 'allocAt' => [sub { + my ($data) = @_; - ACME::Bare::Metal::deallocate($$scope->{'base'}->[0], $$scope->{'size'}->[0]); - }, ['func', 'sys .asm .free'], 'active'], - }; + my $addr = popInt($data); + my $size = popInt($data); + my $block = ACME::Bare::Metal::allocateAt($size, $addr); - push @$data, [enstruct($$scope)]; - }, ['func', 'sys .asm .alloc'], 'active'], + push @$data, [enstruct(constructBlock($block, $size))]; + }, ['func', 'sys .asm .alloc'], 'active'], 'poke' => [sub { my ($data, $scope) = @_; diff --git a/interpreter/ElymasGlobal.pm b/interpreter/ElymasGlobal.pm index 1a72a0e..66fedf1 100644 --- a/interpreter/ElymasGlobal.pm +++ b/interpreter/ElymasGlobal.pm @@ -66,6 +66,34 @@ our $global = { }, ['func', Dumper(\@code)]]; } }, ['func', '}'], 'quote'], + "}'" => [sub { + my ($data, $scope) = @_; + + --$quoted; + + my @code; + while(1) { + my $t = pop @$data or die "Stack underflow"; + last if($t->[1] eq 'tok' and $t->[0] eq '{'); + + unshift @code, $t; + }; + + if($quoted) { + push @$data, [sub { + my ($data, $scope) = @_; + push @$data, [sub { + my ($data) = @_; + interpretCode(\@code, $data, $scope); + }, ['func', Dumper(\@code)]]; + }, ['func', 'func-quoted'], \@code]; + } else { + push @$data, [sub { + my ($data) = @_; + interpretCode(\@code, $data, $scope); + }, ['func', Dumper(\@code)]]; + } + }, ['func', '}'], 'quote'], 'quoted' => [sub { my ($data, $scope) = @_; push @$data, [$quoted? 1: 0, 'int']; @@ -142,6 +170,8 @@ our $global = { # TODO permitted for now } elsif(ref($type) eq 'ARRAY' and $type->[0] eq 'array') { # TODO permitted for now + } elsif(ref($type) eq 'ARRAY' and $type->[0] eq 'struct') { + # TODO permitted for now } else { die "mismatched types in array: " . Dumper($type) unless typeEqual($type, $t->[1]); } @@ -175,7 +205,7 @@ our $global = { $member = $member->[0]; die "not a struct during member dereference in $struct" unless $struct->[1]->[0] eq 'struct'; - die "requested member $member is not in fact existent in " . Dumper($struct, $member) unless exists $struct->[1]->[1]->{$member}; + die Dumper($struct, $member) . "Cannot resolve requested member $member" unless exists $struct->[1]->[1]->{$member}; push @$data, $struct->[0]->{$member}; execute($data, $scope) if($data->[-1]->[2] eq 'active'); @@ -187,7 +217,7 @@ our $global = { my $struct = pop @$data; die "not a struct during member dereference in $struct" unless $struct->[1]->[0] eq 'struct'; - die "requested member $member is not in fact existent in " . Dumper($struct, $member) unless exists $struct->[1]->[1]->{$member}; + die Dumper($struct, $member) . "Cannot resolve requested member $member" unless exists $struct->[1]->[1]->{$member}; push @$data, $struct->[0]->{$member}; }, ['func', '.|'], 'active'], @@ -362,6 +392,27 @@ our $global = { my $d = pop @$data or die "Stack underflow"; die Dumper($d); # , $scope); }, ['func', 'die'], 'active'], + 'keys' => [sub { + my ($data, $scope) = @_; + + my $s = pop @$data or die "Stack underflow"; + + if(ref($s->[1]) eq 'ARRAY' and $s->[1]->[0] eq 'struct') { + my @keys = keys %{$s->[1]->[1]}; + + push @$data, [[map { [$_, 'string'] } @keys], ['array', '[]', [['range', 0, $#keys]], ['string']]]; + } else { + die "keys not supported on this value: " . Dumper($s); + } + }, ['func', 'keys'], 'active'], + 'strToUTF8Bytes' => [sub { + my ($data, $scope) = @_; + + my $str = popString($data); + + my @res = map { [ord, 'int'] } split //, $str; + push @$data, [\@res, ['array', 'from strToUTF8Bytes', [['range', 0, $#res]], ['int']]]; + }, ['func', 'strToUTF8Bytes'], 'active'], # stuff from J 'sig' => [sub { @@ -474,7 +525,7 @@ our $global = { push @$data, $a->[0]->{'dom'}; execute($data, $scope) if($data->[-1]->[2] eq 'active'); } else { - die "dom not supportde on this value: " . Dumper($a); + die "dom not supported on this value: " . Dumper($a); } }, ['func', 'dom'], 'active'], 'exe' => [sub { @@ -535,12 +586,12 @@ installGlobal2IntFunction('xor', sub { return ($_[0] xor $_[1])? 1: 0 }); installGlobal2IntFunction('nxor', sub { return (not ($_[0] xor $_[1]))? 1: 0 }); installGlobal2IntFunction('nor', sub { return (not ($_[0] or $_[1]))? 1: 0 }); -installGlobal2IntFunction('band', sub { return $_[0] & $_[1] }); -installGlobal2IntFunction('bnand', sub { return ~($_[0] & $_[1]) }); -installGlobal2IntFunction('bor', sub { return $_[0] | $_[1] }); -installGlobal2IntFunction('bxor', sub { return $_[0] ^ $_[1] }); -installGlobal2IntFunction('bnxor', sub { return ~($_[0] ^ $_[1]) }); -installGlobal2IntFunction('bnor', sub { return ~($_[0] | $_[1]) }); +installGlobal2IntFunction('band', sub { return (0 + $_[0]) & (0 + $_[1]) }); +installGlobal2IntFunction('bnand', sub { return ~((0 + $_[0]) & (0 + $_[1])) }); +installGlobal2IntFunction('bor', sub { return (0 + $_[0]) | (0 + $_[1]) }); +installGlobal2IntFunction('bxor', sub { return (0 + $_[0]) ^ (0 + $_[1]) }); +installGlobal2IntFunction('bnxor', sub { return ~((0 + $_[0]) ^ (0 + $_[1])) }); +installGlobal2IntFunction('bnor', sub { return ~((0 + $_[0]) | (0 + $_[1])) }); installGlobal2IntFunction('eq', sub { return ($_[0] == $_[1])? 1: 0 }); installGlobal2IntFunction('neq', sub { return ($_[0] != $_[1])? 1: 0 }); @@ -553,7 +604,7 @@ installGlobal2IntFunction('gcd', sub { my ($a, $b) = @_; ($a, $b) = ($b, $a % $b installGlobal1IntFunction('neg', sub { return -$_[0] }); installGlobal1IntFunction('not', sub { return not $_[0] }); -installGlobal1IntFunction('bnot', sub { return ~$_[0] }); +installGlobal1IntFunction('bnot', sub { return ~(0 + $_[0]) }); installGlobal1IntFunction('abs', sub { return abs $_[0] }); # FIXME: this API is ugly diff --git a/interpreter/ElymasSys.pm b/interpreter/ElymasSys.pm index d403bd6..2b7132f 100644 --- a/interpreter/ElymasSys.pm +++ b/interpreter/ElymasSys.pm @@ -107,7 +107,7 @@ sub createFile { die "read failed: $!" unless defined $ret; push @$data, [$buf, 'string']; - }, ['func', 'sys .file .writestr'], 'active'], + }, ['func', 'sys .file .readstr'], 'active'], 'write' => [sub { my ($data) = @_; diff --git a/interpreter/Makefile b/interpreter/Makefile index 278a9b0..d0b43c1 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -20,3 +20,11 @@ generate-test: 2> "test/$$f.err.correct" | tee "test/$$f.correct"; \ sleep 1; \ done + +generate-test-fast: + mkdir -p test + for f in $$(ls ../examples/working); do \ + echo $$f; \ + echo Input | ( cd ../examples/working; ../../interpreter/elymas "$$f"; echo ) \ + 2> "test/$$f.err.correct" | tee "test/$$f.correct"; \ + done @@ -1,4 +1,7 @@ -Expressions: +RegEx stuff: http://sebfisch.github.com/haskell-regexp/ + += Expressions = + 0 1 2... -> just push themselves on the stack "string" -> pushes "string" on the stack <non-bareword><bareword> -> "bareword" <non-bareword> @@ -9,6 +12,7 @@ Expressions: bareword -> lookup "bareword" in current scopes -> passive -> push value on the stack -> active -> call value on current stack + -> quote -> call value on current stack, even in quoted mode / -> nop "string" | -> resolve "string" in current scope, push value @@ -71,7 +75,8 @@ _<digits> -> copy stack contents according to digits A->int B->int add -> B->A->int A->int A->int add -> A->int -Characters: += Characters = + !: <open> ": string quote #: line comment @@ -107,3 +112,44 @@ a-z: bareword characters |: passify }: quote end ~: <open> + + += Memory Management = + +Inspiration: http://wiki.luajit.org/new-garbage-collector + +0x6???????????: Heap memory ("16 TB should be enough for everyone.") +0x5???????????: GC black bitmap +0x4???????????: GC allocation maps + +Large set of reachable, old objects +Large set of unreachable, new objects +Small set in between + + +== Object Memory Layout == + +=== Int === +* Length in bytes (including header, always 16) + bit 63-60: 0 0 0 0 + bit 59: reserved for GC +* value + +=== String === +* Length in bytes (including header) + bit 63-60: 0 0 0 1 + bit 59: reserved for GC +* hash (0 if not yet calculated) +* Exact length +* data (UTF-8) + +=== Struct === +* Length in bytes (including header) + bit 63-60: 0 0 1 0 + bit 59: reserved for GC + bit 58: parent pointer exists (for scopes) + bit 57: extension area pointer exists +* hashtable name -> offset including headers +* parent scope (0 if no parent) +* extension area pointer (0 if no extra members (yet)) +* data |
