M2000 Compiler Task: Difference between revisions

Content added Content deleted
Line 19: Line 19:
Set Fast !
Set Fast !
Module lexical_analyzer (a$){
Module lexical_analyzer (a$){
lim=Len(a$)
lim=Len(a$)
LineNo=1
LineNo=1
ColumnNo=1
ColumnNo=1
Document Output$
Document Output$
Buffer Scanner as Integer*lim
Buffer Scanner as Integer*lim
Return Scanner, 0:=a$
Return Scanner, 0:=a$
offset=0
offset=0
buffer1$=""
buffer1$=""
flag_rem=true
flag_rem=true
Ahead=lambda Scanner (a$, offset)->{
Ahead=lambda Scanner (a$, offset)->{
=false
=false
Try {
Try {
\\ second parameter is the offset in buffer units
// second parameter is the offset in buffer units
\\ third parameter is length in bytes
// third parameter is length in bytes
=Eval$(Scanner, offset,2*len(a$))=a$
=Eval$(Scanner, offset,2*len(a$))=a$
}
}
}
}
Ahead2=lambda Scanner (a$, offset)->{
Ahead2=lambda Scanner (a$, offset)->{
=false
=false
Try {
Try {
=Eval$(Scanner, offset,2) ~ a$
=Eval$(Scanner, offset,2) ~ a$
}
}
}
}
const nl$=chr$(13)+chr$(10), quo$="""", er$="@", Ansi=3
const nl$=chr$(13)+chr$(10), quo$="""", er$="@", Ansi=3
Try {
Try {
Do
Do
If Ahead("/*", offset) Then {
If Ahead("/*", offset) Then {
offset+=2 : ColumnNo+=2
offset+=2 : ColumnNo+=2
While not Ahead("*/", offset)
While not Ahead("*/", offset)
If Ahead(nl$, offset) Then
If Ahead(nl$, offset) Then
lineNo++: ColumnNo=1 : offset+=2
lineNo++: ColumnNo=1 : offset+=2
Else
Else
offset++ : ColumnNo++
offset++ : ColumnNo++
End If
End If
if offset>lim then
if offset>lim then
Error "End-of-file in comment. Closing comment characters not found"+er$
Error "End-of-file in comment. Closing comment characters not found"+er$
End if
End if
End While
End While
offset+=2 : ColumnNo+=2
offset+=2 : ColumnNo+=2
} Else.if Ahead(nl$, offset) Then{
} Else.if Ahead(nl$, offset) Then{
LineNo++: ColumnNo=1
LineNo++: ColumnNo=1
offset+=2
offset+=2
} Else.if Ahead(quo$, offset) Then {
} Else.if Ahead(quo$, offset) Then {
Output$=format$("{0::-10}{1::-10} ", LineNo, ColumnNo)
Output$=format$("{0::-10}{1::-10} ", LineNo, ColumnNo)
offset++ : ColumnNo++
offset++ : ColumnNo++
strin=offset
strin=offset
While not Ahead(quo$, offset)
While not Ahead(quo$, offset)
If Ahead("/", offset) Then
If Ahead("/", offset) Then
offset+=2 : ColumnNo+=2
offset+=2 : ColumnNo+=2
else
else
offset++ : ColumnNo++
offset++ : ColumnNo++
End if
End if
checkerror()
checkerror()
End While
End While
Output$="String "+quote$(Eval$(Scanner, strin, (offset-strin)*2))+nl$
Output$="String "+quote$(Eval$(Scanner, strin, (offset-strin)*2))+nl$
offset++ : ColumnNo++
offset++ : ColumnNo++
} Else.if Ahead("'", offset) Then {
} Else.if Ahead("'", offset) Then {
Output$=format$("{0::-10}{1::-10} ", LineNo, ColumnNo)
Output$=format$("{0::-10}{1::-10} ", LineNo, ColumnNo)
offset++ : ColumnNo++
offset++ : ColumnNo++
strin=offset
strin=offset
While not Ahead("'", offset)
While not Ahead("'", offset)
If Ahead("/", offset) Then
If Ahead("/", offset) Then
offset+=2 : ColumnNo+=2
offset+=2 : ColumnNo+=2
else
else
offset++ : ColumnNo++
offset++ : ColumnNo++
End if
End if
checkerror()
checkerror()
End While
End While
lit$=format$(Eval$(Scanner, strin, (offset-strin)*2))
lit$=format$(Eval$(Scanner, strin, (offset-strin)*2))
select case len(lit$)
select case len(lit$)
case 1
case 1
Output$="Integer "+str$(asc(lit$),0)+nl$
Output$="Integer "+str$(asc(lit$),0)+nl$
case >1
case >1
{Error "Multi-character constant."+er$}
{Error "Multi-character constant."+er$}
case 0
case 0
{Error "Empty character constant."+er$}
{Error "Empty character constant."+er$}
end select
end select
offset++ : ColumnNo++
offset++ : ColumnNo++
} Else.if Ahead2("[a-z]", offset) Then {
} Else.if Ahead2("[a-z]", offset) Then {
strin=offset
strin=offset
Output$=format$("{0::-10}{1::-10} ", LineNo, ColumnNo)
Output$=format$("{0::-10}{1::-10} ", LineNo, ColumnNo)
offset++ : ColumnNo++
offset++ : ColumnNo++
While Ahead2("[a-zA-Z0-9_]", offset)
While Ahead2("[a-zA-Z0-9_]", offset)
offset++ : ColumnNo++
offset++ : ColumnNo++
End While
End While
Keywords(Eval$(Scanner, strin, (offset-strin)*2))
Keywords(Eval$(Scanner, strin, (offset-strin)*2))
} Else.if Ahead2("[0-9]", offset) Then {
} Else.if Ahead2("[0-9]", offset) Then {
strin=offset
strin=offset
Output$=format$("{0::-10}{1::-10} Integer ", LineNo, ColumnNo)
Output$=format$("{0::-10}{1::-10} Integer ", LineNo, ColumnNo)
offset++ : ColumnNo++
offset++ : ColumnNo++
While Ahead2("[0-9]", offset)
While Ahead2("[0-9]", offset)
offset++ : ColumnNo++
offset++ : ColumnNo++
End While
End While
if Ahead2("[a-zA-Z_]", offset) then
if Ahead2("[a-zA-Z_]", offset) then
{Error " Invalid number. Starts like a number, but ends in non-numeric characters."+er$}
{Error " Invalid number. Starts like a number, but ends in non-numeric characters."+er$}
else
else
Output$=Eval$(Scanner, strin, (offset-strin)*2)+nl$
Output$=Eval$(Scanner, strin, (offset-strin)*2)+nl$
end if
end if
} Else {
} Else {
Symbols(Eval$(Scanner, Offset, 2))
Symbols(Eval$(Scanner, Offset, 2))
offset++ : ColumnNo++
offset++ : ColumnNo++
}
}
Until offset>=lim
Until offset>=lim
}
}
er1$=leftpart$(error$,er$)
er1$=leftpart$(error$,er$)
if er1$<>"" then
if er1$<>"" then
Print
Print
Report "Error:"+er1$
Report "Error:"+er1$
Output$="(Error)"+nl$+"Error:"+er1$
Output$="(Error)"+nl$+"Error:"+er1$
else
else
Output$=format$("{0::-10}{1::-10}", LineNo, ColumnNo)+" End_of_Input"+nl$
Output$=format$("{0::-10}{1::-10}", LineNo, ColumnNo)+" End_of_Input"+nl$
end if
end if
Push Output$
Push Output$
Exit
Exit
Clipboard Output$
Clipboard Output$
Save.Doc Output$, "lex.t", Ansi
Save.Doc Output$, "lex.t", Ansi
document lex$
document lex$
Load.Doc lex$,"lex.t", Ansi
Load.Doc lex$,"lex.t", Ansi
Report lex$
Report lex$
Sub Keywords(a$)
Sub Keywords(a$)
select case a$
case "if"
select case a$
case "if"
a$="Keyword_if"
a$="Keyword_if"
case "else"
case "else"
a$="Keyword_else"
a$="Keyword_else"
case "while"
case "while"
a$="Keyword_while"
a$="Keyword_while"
case "print"
case "print"
a$="Keyword_print"
a$="Keyword_print"
case "putc"
case "putc"
a$="Keyword_putc"
a$="Keyword_putc"
else case
else case
a$="Identifier "+a$
a$="Identifier "+a$
end select
end select
Output$=a$+nl$
Output$=a$+nl$
End sub
End sub
Sub Symbols(a$)
Sub Symbols(a$)
select case a$
case " ", chr$(9)
select case a$
case " ", chr$(9)
a$=""
a$=""
case "("
case "("
a$="LeftParen"
a$="LeftParen"
case ")"
case ")"
a$="RightParen"
a$="RightParen"
case "{"
case "{"
a$="LeftBrace"
a$="LeftBrace"
case "}"
case "}"
a$="RightBrace"
a$="RightBrace"
case ";"
case ";"
a$="Semicolon"
a$="Semicolon"
case ","
case ","
a$="Comma"
a$="Comma"
case "*"
case "*"
a$="Op_multiply"
a$="Op_multiply"
case "/"
case "/"
a$="Op_divide"
a$="Op_divide"
case "+"
case "+"
a$="Op_add"
a$="Op_add"
case "-"
case "-"
a$="Op_subtract"
a$="Op_subtract"
case "%"
case "%"
a$="Op_mod"
a$="Op_mod"
case "<"
case "<"
{ if Ahead("=", offset+1) Then
{ if Ahead("=", offset+1) Then
offset++
offset++
a$="Op_lessequal"
a$="Op_lessequal"
ColumnNo++
ColumnNo++
else
else
a$="Op_less"
a$="Op_less"
end if
end if
}
}
case ">"
case ">"
{ if Ahead("=", offset+1) Then
{ if Ahead("=", offset+1) Then
offset++
offset++
ColumnNo++
ColumnNo++
a$="Op_greaterequal"
a$="Op_greaterequal"
else
else
a$="Op_greater"
a$="Op_greater"
end if
end if
}
}
case "="
case "="
{ if Ahead("=", offset+1) Then
{ if Ahead("=", offset+1) Then
offset++
offset++
ColumnNo++
ColumnNo++
a$="Op_equal"
a$="Op_equal"
else
else
a$="Op_assign"
a$="Op_assign"
end if
end if
}
}
case "!"
case "!"
{ if Ahead("=", offset+1) Then
{ if Ahead("=", offset+1) Then
offset++
offset++
ColumnNo++
ColumnNo++
a$="Op_notequal"
a$="Op_notequal"
else
else
a$="Op_not"
a$="Op_not"
end if
end if
}
}
case "&"
case "&"
{ if Ahead("&", offset+1) Then
{ if Ahead("&", offset+1) Then
offset++
offset++
ColumnNo++
ColumnNo++
a$="Op_and"
a$="Op_and"
else
else
a$=""
a$=""
end if
end if
}
}
case "|"
case "|"
{ if Ahead("|", offset+1) Then
{ if Ahead("|", offset+1) Then
offset++
offset++
ColumnNo++
ColumnNo++
a$="Op_or"
a$="Op_or"
else
else
a$=""
a$=""
end if
end if
}
}
else case
else case
{Error "Unrecognized character."+er$}
{Error "Unrecognized character."+er$}
end select
end select
if a$<>"" then
if a$<>"" then
Output$=format$("{0::-10}{1::-10} ", LineNo, ColumnNo)+a$+nl$
Output$=format$("{0::-10}{1::-10} ", LineNo, ColumnNo)+a$+nl$
end if
end if
End Sub
End Sub
Sub checkerror()
Sub checkerror()
if offset>lim then {
if offset>lim then {
Error "End-of-line while scanning string literal. Closing string character not found before end-of-line."+er$
Error "End-of-line while scanning string literal. Closing string character not found before end-of-line."+er$
} else.if Ahead(nl$,offset) then {
} else.if Ahead(nl$,offset) then {
Error "End-of-file while scanning string literal. Closing string character not found."+er$
Error "End-of-file while scanning string literal. Closing string character not found."+er$
}
}
End Sub
End Sub
}
}
Module syntax_analyzer (b$){
Module syntax_analyzer (b$){
enum tokens {
enum tokens {
Op_add, Op_subtract, Op_not=5, Op_multiply=10, Op_divide, Op_mod,
Op_add, Op_subtract, Op_not=5, Op_multiply=10, Op_divide, Op_mod,
Op_negate, Op_less, Op_lessequal, Op_greater, Op_greaterequal,
Op_negate, Op_less, Op_lessequal, Op_greater, Op_greaterequal,
Op_equal, Op_notequal, Op_and, Op_or, Op_assign=100, Keyword_if=110,
Op_equal, Op_notequal, Op_and, Op_or, Op_assign=100, Keyword_if=110,
Keyword_else, Keyword_while, Keyword_print, Keyword_putc, LeftParen, RightParen,
Keyword_else, Keyword_while, Keyword_print, Keyword_putc, LeftParen, RightParen,
LeftBrace, RightBrace, Semicolon, Comma, Identifier, Integer, String, End_of_input
LeftBrace, RightBrace, Semicolon, Comma, Identifier, Integer, String, End_of_input
}
}
Inventory precedence=Op_multiply:=13, Op_divide:=13, Op_mod:=13, Op_add:=12, Op_subtract:=12
Append precedence, Op_negate:=14, Op_not:=14, Op_less:=10, Op_lessequal:=10, Op_greater:=10
Inventory precedence=Op_multiply:=13, Op_divide:=13, Op_mod:=13, Op_add:=12, Op_subtract:=12
Append precedence, Op_greaterequal:=10, Op_equal:=9, Op_notequal:=9, Op_assign:=-1, Op_and:=5
Append precedence, Op_negate:=14, Op_not:=14, Op_less:=10, Op_lessequal:=10, Op_greater:=10
Append precedence, Op_greaterequal:=10, Op_equal:=9, Op_notequal:=9, Op_assign:=-1, Op_and:=5
Append precedence, Op_or:=4
Append precedence, Op_or:=4
Inventory symbols=Op_multiply:="Multiply", Op_divide:="Divide", Op_mod:="Mod", Op_add:="Add"
Append symbols, Op_negate:="Negate", Op_not:="Not", Op_less:="Less", Op_subtract:="Subtract"
Append symbols, Op_lessequal:="LessEqual", Op_greater:="Greater", Op_greaterequal:="GreaterEqual"
Inventory symbols=Op_multiply:="Multiply", Op_divide:="Divide", Op_mod:="Mod", Op_add:="Add"
Append symbols, Op_equal:="Equal", Op_notequal:="NotEqual", Op_and:="And", Op_or:="Or"
Append symbols, Op_negate:="Negate", Op_not:="Not", Op_less:="Less", Op_subtract:="Subtract"
Append symbols, Op_lessequal:="LessEqual", Op_greater:="Greater", Op_greaterequal:="GreaterEqual"
def lineNo, ColumnNo, m, line$, a, lim, cur=-1
Append symbols, Op_equal:="Equal", Op_notequal:="NotEqual", Op_and:="And", Op_or:="Or"
const nl$=chr$(13)+chr$(10), Ansi=3
Dim lex$()
def lineNo, ColumnNo, m, line$, a, lim, cur=-1
lex$()=piece$(b$,chr$(13)+chr$(10))
const nl$=chr$(13)+chr$(10), Ansi=3
lim=dimension(lex$(),1)-1
Dim lex$()
op=End_of_input
lex$()=piece$(b$,chr$(13)+chr$(10))
flush
lim=dimension(lex$(),1)-1
k=0
op=End_of_input
Try {
flush
push (,) ' Null
k=0
getone(&op)
Try {
repeat
push (,) ' Null
stmt(&op)
getone(&op)
shift 2 ' swap two top items
repeat
push ("Sequence", array, array)
stmt(&op)
k++
shift 2 ' swap two top items
until op=End_of_Input
push ("Sequence", array, array)
}
k++
er$=error$
until op=End_of_Input
if er$<>"" then print er$ : flush: break
}
Print "Ast"
er$=error$
Document Output$
if er$<>"" then print er$ : flush: break
prt_ast()
Print "Ast"
Push Output$
Document Output$
exit
prt_ast()
clipboard Output$
Save.Doc Output$, "parse.t", Ansi
Push Output$
exit
document parse$
clipboard Output$
Load.Doc parse$,"parse.t", Ansi
Save.Doc Output$, "parse.t", Ansi
Report parse$
document parse$
sub prt_ast(t)
Load.Doc parse$,"parse.t", Ansi
if len(t)<1 then
Report parse$
Output$=";"+nl$
else.if len(t)=3 then
sub prt_ast(t)
Output$=t#val$(0) +nl$
if len(t)<1 then
prt_ast(t#val(1)) : prt_ast(t#val(2))
Output$=";"+nl$
else
else.if len(t)=3 then
Output$=t#val$(0) +nl$
Output$=t#val$(0) +nl$
end if
prt_ast(t#val(1)) : prt_ast(t#val(2))
end sub
else
sub expr(p) ' only a number
Output$=t#val$(0) +nl$
local x=(,), prev=op
end if
if op>=Identifier then
end sub
x=(line$,)
sub expr(p) ' only a number
getone(&op)
local x=(,), prev=op
else.if op=LeftParen then
if op>=Identifier then
paren_exp()
x=(line$,)
x=array
getone(&op)
else.if op<10 then
else.if op=LeftParen then
getone(&op)
paren_exp()
expr(precedence(int(Op_negate)))
x=array
read local y
if prev=Op_add then
else.if op<10 then
getone(&op)
x=y
expr(precedence(int(Op_negate)))
else
read local y
if prev=Op_subtract then prev=Op_negate
if prev=Op_add then
x=(symbols(prev), y,(,))
x=y
End if
else
else
if prev=Op_subtract then prev=Op_negate
{error "??? "+eval$(op)}
x=(symbols(prev), y,(,))
end if
End if
local prec
else
while exist(precedence, int(op))
{error "??? "+eval$(op)}
prev=op : prec=eval(precedence)
end if
if prec<14 and prec>=p else exit
local prec
getone(&op)
while exist(precedence, int(op))
expr(prec+1) ' all operators are left associative (use prec for right a.)
prev=op : prec=eval(precedence)
x=(symbols(int(prev)), x, array)
if prec<14 and prec>=p else exit
End While
getone(&op)
Push x
expr(prec+1) ' all operators are left associative (use prec for right a.)
end sub
x=(symbols(int(prev)), x, array)
sub paren_exp()
End While
expected(LeftParen)
Push x
getone(&op)
end sub
expr(0)
sub paren_exp()
expected(RightParen)
expected(LeftParen)
getone(&op)
getone(&op)
end sub
expr(0)
sub stmt(&op)
expected(RightParen)
local t=(,)
getone(&op)
if op=Identifier then
end sub
t=(line$)
sub stmt(&op)
getone(&op)
local t=(,)
expected(Op_assign)
if op=Identifier then
getone(&op)
t=(line$)
expr(0)
getone(&op)
read local rightnode
expected(Op_assign)
Push ("Assign",t,rightnode)
getone(&op)
expected(Semicolon)
expr(0)
getone(&op)
read local rightnode
else.if op=Semicolon then
Push ("Assign",t,rightnode)
getone(&op)
expected(Semicolon)
Push (";",)
getone(&op)
else.if op=Keyword_print then
else.if op=Semicolon then
getone(&op)
getone(&op)
expected(LeftParen)
Push (";",)
repeat
else.if op=Keyword_print then
getone(&op)
getone(&op)
if op=String then
expected(LeftParen)
Push ("Prts",(line$,),(,))
repeat
getone(&op)
getone(&op)
else
if op=String then
expr(0)
Push ("Prti", array,(,))
Push ("Prts",(line$,),(,))
getone(&op)
end if
else
t=("Sequence", t, array)
expr(0)
until op<>Comma
Push ("Prti", array,(,))
expected(RightParen)
end if
getone(&op)
t=("Sequence", t, array)
expected(Semicolon)
until op<>Comma
getone(&op)
expected(RightParen)
push t
getone(&op)
else.if op=Keyword_while then
expected(Semicolon)
getone(&op)
getone(&op)
paren_exp()
push t
stmt(&op)
else.if op=Keyword_while then
shift 2
getone(&op)
Push ("While",array, array)
paren_exp()
else.if op=Keyword_if then
stmt(&op)
getone(&op)
shift 2
paren_exp()
Push ("While",array, array)
stmt(&op)
else.if op=Keyword_if then
local s2=(,)
getone(&op)
if op=Keyword_else then
paren_exp()
getone(&op)
stmt(&op)
stmt(&op)
local s2=(,)
read s2
if op=Keyword_else then
end if
getone(&op)
shift 2
stmt(&op)
Push ("If",array ,("If",array,s2))
read s2
else.if op=Keyword_putc then
end if
getone(&op)
shift 2
paren_exp()
Push ("Prtc",array,t)
Push ("If",array ,("If",array,s2))
else.if op=Keyword_putc then
expected(Semicolon)
getone(&op)
getone(&op)
paren_exp()
else.if op=LeftBrace then
Push ("Prtc",array,t)
Brace()
expected(Semicolon)
else
getone(&op)
error "Unkown Op"
else.if op=LeftBrace then
end if
Brace()
end sub
else
Sub Brace()
error "Unkown Op"
getone(&op)
end if
while op<>RightBrace and op<>End_of_input
end sub
stmt(&op)
Sub Brace()
t=("Sequence", t, array)
getone(&op)
end while
while op<>RightBrace and op<>End_of_input
expected(RightBrace)
stmt(&op)
getone(&op)
t=("Sequence", t, array)
push t
end while
End Sub
Sub expected(what)
expected(RightBrace)
getone(&op)
if not op=what then {Error "Expected "+eval$(what)+str$(LineNo)+","+Str$(ColumnNo)}
push t
End Sub
End Sub
sub getone(&op)
Sub expected(what)
op=End_of_input
if not op=what then {Error "Expected "+eval$(what)+str$(LineNo)+","+Str$(ColumnNo)}
while cur<lim
End Sub
cur++
sub getone(&op)
line$=trim$(lex$(cur))
op=End_of_input
if line$<>"" then exit
while cur<lim
end while
cur++
if cur=lim then exit sub
line$=trim$(lex$(cur))
LineNo=Val(line$,"int",m)
line$=mid$(line$, m)
if line$<>"" then exit
end while
ColumnNo=Val(line$,"int",m)
if cur=lim then exit sub
line$=trim$(mid$(line$, m))
LineNo=Val(line$,"int",m)
Rem : Print LineNo, ColumnNo
m=instr(line$," ")
line$=mid$(line$, m)
ColumnNo=Val(line$,"int",m)
if m>0 then op=Eval("."+leftpart$(line$, " ")) else op=Eval("."+line$)
line$=trim$(mid$(line$, m))
end sub
Rem : Print LineNo, ColumnNo
m=instr(line$," ")
if m>0 then op=Eval("."+leftpart$(line$, " ")) else op=Eval("."+line$)
end sub
}
}
Module CodeGenerator (s$){
Module CodeGenerator (s$){
Function code$(op$) {
Function code$(op$) {
=format$("{0::-6} {1}", pc, op$)
=format$("{0::-6} {1}", pc, op$)
pc++
pc++
}
}
Function code2$(op$, n$) {
Function code2$(op$, n$) {
=format$("{0::-6} {1} {2}", pc, op$, n$)
=format$("{0::-6} {1} {2}", pc, op$, n$)
pc+=5
pc+=5
}
}
Function code3$(op$,pc, st, ed) {
Function code3$(op$,pc, st, ed) {
=format$("{0::-6} {1} ({2}) {3}", pc, op$, ed-st-1, ed)
=format$("{0::-6} {1} ({2}) {3}", pc, op$, ed-st-1, ed)
}
}
Enum tok {
Enum tok {
gneg, gnot, gmul, gdiv, gmod, gadd, gle, gsub, glt
gle, ggt, gge, geq, gne, gand, gor, gprtc, gprti, gprts,
gneg, gnot, gmul, gdiv, gmod, gadd, gle, gsub, glt
gle, ggt, gge, geq, gne, gand, gor, gprtc, gprti, gprts,
gif, gwhile, gAssign, gSeq, gstring, gidentifier, gint, gnone
gif, gwhile, gAssign, gSeq, gstring, gidentifier, gint, gnone
}
}
\\ Inventories are lists with keys, or keys/data (key must be unique)
\\ there is one type more the Invetory Queue which get same keys.
// Inventories are lists with keys, or keys/data (key must be unique)
\\ But here not used.
// there is one type more the Invetory Queue which get same keys.
Inventory symb="Multiply":=gmul, "Divide":=gdiv, "Mod":=gmod, "Add":=gadd
// But here not used.
Append symb, "Negate":=gneg, "Not":=gnot,"Less":=glt,"Subtract":=gsub
Append symb, "LessEqual":=gle, "Greater":=ggt, "GreaterEqual":=gge, "Sequence":=gSeq
Inventory symb="Multiply":=gmul, "Divide":=gdiv, "Mod":=gmod, "Add":=gadd
Append symb, "Equal":=geq, "NotEqual":=gne, "And":=gand, "Or":=gor, "While":=gwhile
Append symb, "Negate":=gneg, "Not":=gnot,"Less":=glt,"Subtract":=gsub
Append symb, "Prtc":=gprtc,"Prti":=gprti,"Prts":=gprts, "Assign":=gAssign, "If":=gif
Append symb, "LessEqual":=gle, "Greater":=ggt, "GreaterEqual":=gge, "Sequence":=gSeq
Append symb, "String":=gstring, "Identifier":=gidentifier, "Integer":=gint, ";", gnone
Append symb, "Equal":=geq, "NotEqual":=gne, "And":=gand, "Or":=gor, "While":=gwhile
Append symb, "Prtc":=gprtc,"Prti":=gprti,"Prts":=gprts, "Assign":=gAssign, "If":=gif
Inventory DataSet
Append symb, "String":=gstring, "Identifier":=gidentifier, "Integer":=gint, ";", gnone
\\ We set string as key. key maybe an empty string, a string or a number.
\\ so we want eash string to saved one time only.
Inventory Strings
Inventory DataSet
// We set string as key. key maybe an empty string, a string or a number.
Const nl$=chr$(13)+chr$(10), Ansi=3
// so we want eash string to saved one time only.
Def z$, lim, line$, newvar_ok, i=0
Inventory Strings
Document message$=nl$
Global pc \\ functions have own scope, so we make it global, for this module, and childs.
Const nl$=chr$(13)+chr$(10), Ansi=3
Dim lines$()
Def z$, lim, line$, newvar_ok, i=0
s$=filter$(s$,chr$(9)) \\ exclude tabs
Document message$=nl$
Lines$()=piece$(s$,nl$) \\ break to lines
Global pc // functions have own scope, so we make it global, for this module, and childs.
lim=len(Lines$())
Flush ' empty stack (there is a current stack of values which we use here)
Dim lines$()
Load_Ast()
s$=filter$(s$,chr$(9)) // exclude tabs
If not stack.size=1 Then Flush : Error "Ast not loaded"
Lines$()=piece$(s$,nl$) // break to lines
AST=array \\ pop the array from stack
lim=len(Lines$())
Document Assembly$, Header$
Flush ' empty stack (there is a current stack of values which we use here)
\\ all lines of assembly goes to stack. Maybe not in right order.
\\ Push statement push to top, Data statement push to bottom of stack
Load_Ast()
CodeGenerator(Ast)
If not stack.size=1 Then Flush : Error "Ast not loaded"
Data code$("halt") ' append to end of stack
\\ So now we get all data (letters) from stack
AST=array // pop the array from stack
Document Assembly$, Header$
While not empty
Assembly$=letter$+nl$
// all lines of assembly goes to stack. Maybe not in right order.
end while
// Push statement push to top, Data statement push to bottom of stack
\\ So now we have to place them in order
Sort Assembly$
CodeGenerator(Ast)
\\ Let's make the header
Data code$("halt") ' append to end of stack
Header$=format$("Datasize: {0} Strings: {1}", Len(Dataset),Len(strings))
// So now we get all data (letters) from stack
\\ we use an iterator object, str^ is the counter, readonly, but Eval$() use it from object.
While not empty
str=each(strings)
Assembly$=letter$+nl$
While str
end while
Header$=nl$+Eval$(str)
End while
// So now we have to place them in order
Assembly$=nl$
Sort Assembly$
\\ insert to line 1 the Header
// Let's make the header
Insert 1 Assembly$=Header$
Header$=format$("Datasize: {0} Strings: {1}", Len(Dataset),Len(strings))
\\ Also we check for warnings
// we use an iterator object, str^ is the counter, readonly, but Eval$() use it from object.
If len(message$)>2 then Assembly$="Warnings: "+nl$+message$
str=each(strings)
\\ So now we get a report
While str
\\ (at each 3/4 of window's lines, the printing stop and wait for user response, any key)
Header$=nl$+Eval$(str)
Push Assembly$
End while
Exit
Report Assembly$
Assembly$=nl$
// insert to line 1 the Header
Clipboard Assembly$
Save.Doc Assembly$, "code.t", Ansi
Insert 1 Assembly$=Header$
// Also we check for warnings
End
If len(message$)>2 then Assembly$="Warnings: "+nl$+message$
\\ subs have 10000 limit for recursion but can be extended to 1000000 or more.
// So now we get a report
Sub CodeGenerator(t)
// (at each 3/4 of window's lines, the printing stop and wait for user response, any key)
If len(t)=3 then
Push Assembly$
select case t#val(0)
Exit
Case gSeq
Report Assembly$
CodeGenerator(t#val(1)) : CodeGenerator(t#val(2))
Clipboard Assembly$
Case gwhile
Save.Doc Assembly$, "code.t", Ansi
{
End
local spc=pc
// subs have 10000 limit for recursion but can be extended to 1000000 or more.
CodeGenerator(t#val(1))
Sub CodeGenerator(t)
local pc1=pc
pc+=5 ' room for jz
If len(t)=3 then
CodeGenerator(t#val(2))
select case t#val(0)
data code3$("jz",pc1, pc1, pc+5)
Case gSeq
data code3$("jmp",pc, pc, spc)
CodeGenerator(t#val(1)) : CodeGenerator(t#val(2))
pc+=5 ' room for jmp
Case gwhile
}
{
Case gif
local spc=pc
{
CodeGenerator(t#val(1))
CodeGenerator(t#val(1))
local pc1=pc, pc2
local pc1=pc
pc+=5 ' room for jz
pc+=5
CodeGenerator(t#val(2)#val(1))
CodeGenerator(t#val(2))
data code3$("jz",pc1, pc1, pc+5)
If len(t#val(2)#val(2))>0 then
data code3$("jmp",pc, pc, spc)
pc2=pc
pc+=5 ' room for jmp
pc+=5
}
data code3$("jz",pc1, pc1, pc)
Case gif
CodeGenerator(t#val(2)#val(2))
{
data code3$("jmp",pc2, pc2, pc)
CodeGenerator(t#val(1))
else
local pc1=pc, pc2
data code3$("jz",pc1, pc1, pc)
pc+=5
end If
CodeGenerator(t#val(2)#val(1))
}
If len(t#val(2)#val(2))>0 then
Case gAssign
pc2=pc
{
pc+=5
CodeGenerator(t#val(2))
data code3$("jz",pc1, pc1, pc)
local newvar_ok=true
CodeGenerator(t#val(1))
CodeGenerator(t#val(2)#val(2))
data code3$("jmp",pc2, pc2, pc)
}
else
case gneg to gnot, gprtc to gprts
data code3$("jz",pc1, pc1, pc)
CodeGenerator(t#val(1)) : data code$(mid$(eval$(t#val(0)),2))
end If
case gmul to gor
}
{
Case gAssign
CodeGenerator(t#val(1))
{
CodeGenerator(t#val(2))
CodeGenerator(t#val(2))
data code$(mid$(eval$(t#val(0)),2))
local newvar_ok=true
}
CodeGenerator(t#val(1))
End select
}
Else.if len(t)=2 then
case gneg to gnot, gprtc to gprts
select case t#val(0)
CodeGenerator(t#val(1)) : data code$(mid$(eval$(t#val(0)),2))
Case gString
case gmul to gor
{
{
local spos
CodeGenerator(t#val(1))
If exist(strings,t#val$(1)) then
CodeGenerator(t#val(2))
spos=eval(strings!)
data code$(mid$(eval$(t#val(0)),2))
else
}
append strings, t#val$(1)
End select
spos=len(strings)-1
Else.if len(t)=2 then
end If
select case t#val(0)
Push code2$("push",str$(spos,0))
Case gString
}
{
Case gInt
local spos
Push code2$("push",t#val$(1), pc)
If exist(strings,t#val$(1)) then
Case gIdentifier
spos=eval(strings!)
{
else
local ipos
append strings, t#val$(1)
If exist(dataset,t#val$(1)) then
spos=len(strings)-1
ipos=Eval(dataset!) ' return position
end If
else.if newvar_ok then
Push code2$("push",str$(spos,0))
Append dataset, t#val$(1)
}
ipos=len(dataset)-1
Case gInt
else
Push code2$("push",t#val$(1), pc)
message$="Variable "+t#val$(1)+" not initialized"+nl$
Case gIdentifier
end If
{
If newvar_ok then
local ipos
Push code2$("store","["+str$(ipos, 0)+"]")
If exist(dataset,t#val$(1)) then
else
ipos=Eval(dataset!) ' return position
Push code2$("fetch","["+str$(ipos, 0)+"]")
else.if newvar_ok then
end If
Append dataset, t#val$(1)
}
ipos=len(dataset)-1
end select
else
End If
message$="Variable "+t#val$(1)+" not initialized"+nl$
End Sub
Sub Load_Ast()
end If
If i>=lim then Push (,) : exit sub
If newvar_ok then
do
Push code2$("store","["+str$(ipos, 0)+"]")
line$=Trim$(lines$(i))
else
I++
Push code2$("fetch","["+str$(ipos, 0)+"]")
tok$=piece$(line$," ")(0)
end If
until line$<>"" or i>=lim
}
If tok$="Identifier" then
end select
Push (gidentifier,trim$(Mid$(line$,11)))
End If
else.if tok$="Integer" then
End Sub
long n=Val(Mid$(line$,8)) ' check overflow
Sub Load_Ast()
Push (gint, Trim$(Mid$(line$,8)))
If i>=lim then Push (,) : exit sub
else.if tok$="String" then
do
Push (gstring,Trim$(Mid$(line$,7)))
line$=Trim$(lines$(i))
else.if tok$=";" then
I++
Push (,)
tok$=piece$(line$," ")(0)
Else
until line$<>"" or i>=lim
local otok=symb(tok$)
If tok$="Identifier" then
Load_Ast()
Push (gidentifier,trim$(Mid$(line$,11)))
Load_Ast()
else.if tok$="Integer" then
Shift 2
long n=Val(Mid$(line$,8)) ' check overflow
Push (otok,array, array)
Push (gint, Trim$(Mid$(line$,8)))
End If
else.if tok$="String" then
End Sub
Push (gstring,Trim$(Mid$(line$,7)))
else.if tok$=";" then
Push (,)
Else
local otok=symb(tok$)
Load_Ast()
Load_Ast()
Shift 2
Push (otok,array, array)
End If
End Sub
}
}
Module Virtual_Machine_Interpreter (a$){
Module Virtual_Machine_Interpreter (a$){
\\ function to extract string, replacing escape codes.
// function to extract string, replacing escape codes.
Function GetString$(a$) {
Function GetString$(a$) {
s=instr(a$, chr$(34))
s=instr(a$, chr$(34))
m=rinstr(a$,chr$(34))-s
m=rinstr(a$,chr$(34))-s
if m>1 then
if m>1 then
\\ process escape codes
// process escape codes
=format$(mid$(a$, s+1, m-1))
=format$(mid$(a$, s+1, m-1))
else
else
Line 632: Line 648:
}
}
const nl$=chr$(13)+chr$(10)
const nl$=chr$(13)+chr$(10)
\\ we can set starting value to any number n where 0<=n<=232
// we can set starting value to any number n where 0<=n<=232
enum op { halt_=232, add_, sub_, mul_, div_, mod_, not_, neg_, and_, or_, lt_,
enum op { halt_=232, add_, sub_, mul_, div_, mod_, not_, neg_, and_, or_, lt_,
gt_, le_, ge_, ne_, eq_, prts_, prti_, prtc_, store_, fetch_, push_,
gt_, le_, ge_, ne_, eq_, prts_, prti_, prtc_, store_, fetch_, push_,
Line 665: Line 681:
Append func, prtc_:=lambda->{Print #-2, chrcode$(eval(stack_,sp));: Refresh:sp++}
Append func, prtc_:=lambda->{Print #-2, chrcode$(eval(stack_,sp));: Refresh:sp++}
Rem : Form 120, 60 ' change console width X height to run Ascii Mandlebrot example
Rem : Form 120, 60 ' change console width X height to run Ascii Mandlebrot example
\\ change Report with Print #-2, (report stop when scrolling 3/4 of height of console, waiting key or mouse key to continue)
// change Report with Print #-2, (report stop when scrolling 3/4 of height of console, waiting key or mouse key to continue)
Print #-2, "Virtual Assembly Code:"+{
Print #-2, "Virtual Assembly Code:"+{
}+a$
}+a$
Print "Prepare Byte Code"
Print "Prepare Byte Code"
\\ get datasize
// get datasize
a$=rightpart$(a$, "Datasize:")
a$=rightpart$(a$, "Datasize:")
m=0
m=0
data_size=val(a$, "int", m)
data_size=val(a$, "int", m)
a$=mid$(a$, m)
a$=mid$(a$, m)
\\ make stack
// make stack
if data_size>0 then Buffer Clear stack_ as long*data_size
if data_size>0 then Buffer Clear stack_ as long*data_size
\\ dim or redim buffer append 1000 long as is.
// dim or redim buffer append 1000 long as is.
Buffer stack_ as long*(1000+data_size)
Buffer stack_ as long*(1000+data_size)
\\ get strings
// get strings
a$=rightpart$(a$, "Strings:")
a$=rightpart$(a$, "Strings:")
m=0
m=0
strings=val(a$, "int", m)
strings=val(a$, "int", m)
a$=rightpart$(a$, nl$)
a$=rightpart$(a$, nl$)
if strings>0 then
if strings>0 then
Dim strings$(strings)
Dim strings$(strings)
Line 715: Line 733:
Always
Always
Print "Press any key" : Push key$ : Drop
Print "Press any key" : Push key$ : Drop
\\ Prepare VM
// Prepare VM
profiler
let pc=0, sp=len(stack_) div 4
let pc=0, sp=len(stack_) div 4
do
do
Line 722: Line 741:
call local b()
call local b()
until exit_now
until exit_now
Print "done"
Print
Print "", round(Timecount/1000,2),"s"
}
Module Transpiler_to_M2000 (code$){
Print #-2, "Virtual Assembly Code:"+{
}+code$
Print "Prepare M2000 Code"
Flush
prolog$={Global z
Module Halt {Error "stop"}
Module neg {push -number}
Module not {push number=0}
Module and {push sint(binary.and(uint(number), uint(number)))}
Module or {push sint(binary.or(uint(number), uint(number)))}
Module gt {push number<number}
Module lt {push number>number}
Module ge {push number<=number}
Module le {push number>=number}
Module ne {push number<>number}
Module eq {push number=number}
Module mul {push number*number}
Module div {shift 2 : push number div number}
Module mod {shift 2 : push number mod number}
Module sub {shift 2 : push number - number}
Module add {push number + number}
Module prtc {Print #-2, chrcode$(number);:Refresh}
Module prts {Print #-2, format$(var$(number));:Refresh}
Module prti {Print #-2, str$(number, 0);:Refresh}
}
Variable$=lambda$ (many, Name$, glob as boolean=false)->{
if many else exit
if glob then
="Global "+name$+"(0 to "+str$(many,0)+")"+{
}
else
="Dim Base 0, "+name$+"("+str$(many,0)+")"+{
}
end if
}
firstline$=leftpart$(code$,13)
code$=rightpart$(code$, 10)
firstline$=rightpart$(firstline$, ": ")
Vars=val(firstline$)
firstline$=rightpart$(firstline$, ": ")
Strings=val(firstline$)
document feedstrings$
if Strings Then
for i=0 to Strings-1
firstline$=Trim$(leftpart$(code$,13))
code$=rightpart$(code$, 10)
feedstrings$="var$("+str$(i,0)+") = "+firstline$+{
}
next
end if
stack new {
{
g$=leftpart$(code$, " (")
if g$="" or code$="" then
data code$ : code$="": exit
end if
data g$
code$=rightpart$(code$, 41)
loop
}
while not empty{
read g$
code$+=g$
}
}
code$=replace$("]", ")", code$)
code$=replace$("fetch [", "fetch(", code$)
code$=replace$("store [", "store(", code$)
code$=replace$("jmp ", "goto ", code$)
code$=replace$("jz", "if number else ", code$)
Subs$={
End
Sub store(x)
Read var(x)
End Sub
Sub fetch(x)
Push var(x)
End Sub
}
document M2000Code$="Module Run {" +prolog$ + Variable$(Vars,"var")
M2000Code$=Variable$(Strings,"var$", true) +feedstrings$ + "Try {" + {
} + code$+"}"+Subs$+"}"
//clipboard M2000code$
//exit
Inline M2000Code$
Print "Press any key" : Push key$ : Drop
profiler
Run
Print
Print round(Timecount/1000,2),"s"
}
print "choose example",
menu "Mandelbrot", "primes","test case 4", "99 Bottles of Beer", "FizzBuzz", "fibonacci of 44 is 701408733", "12 factorial is 479001600"
if menu then print menu$(menu) else exit
select case menu
case 1
{
Push {
{
/*
This is an integer ascii Mandelbrot generator
*/
left_edge= -420;
right_edge=300;
top_edge=300;
bottom_edge = -300;
x_step=7;
y_step=15;
max_iter=200;
y0 = top_edge;
while (y0 > bottom_edge) {
x0 = left_edge;
while (x0 < right_edge) {
y = 0;
x = 0;
the_char = ' ';
i = 0;
while (i < max_iter) {
x_x = (x * x) / 200;
y_y = (y * y) / 200;
if (x_x + y_y > 800 ) {
the_char = '0' + i;
if (i > 9) {
the_char = '@';
}
i = max_iter;
}
y = x * y / 100 + y0;
x = x_x - y_y + x0;
i = i + 1;
}
putc(the_char);
x0 = x0 + x_step;
}
putc('\n');
y0 = y0 - y_step;
}
}
}
}
case 2
{
push {
/*
Simple prime number generator
*/
count = 1;
n = 1;
limit = 100;
while (n < limit) {
k=3;
p=1;
n=n+2;
while ((k*k<=n) && (p)) {
p=n/k*k!=n;
k=k+2;
}
if (p) {
print(n, " is prime\n");
count = count + 1;
}
}
print("Total primes found: ", count, "\n");
}
}
case 3
{
push {
/*** test printing, embedded \n and comments with lots of '*' ***/ print(42); print("\nHello World\nGood Bye\nok\n"); print("Print a slash n - \\n.\n");
}
}
case 4
{
push {
/* 99 bottles */ bottles = 99; while (bottles > 0) {
print(bottles, " bottles of beer on the wall\n");
print(bottles, " bottles of beer\n");
print("Take one down, pass it around\n");
bottles = bottles - 1;
print(bottles, " bottles of beer on the wall\n\n");
}
}
}
case 5
{
push {
/* FizzBuzz */ i = 1; while (i <= 100) {

if (!(i % 15))
print("FizzBuzz");
else if (!(i % 3))
print("Fizz");
else if (!(i % 5))
print("Buzz");
else
print(i);
print("\n");
i = i + 1;
}
}
}
case 6
{
push {
/* fibonacci of 44 is 701408733 */
n = 44; i = 1; a = 0; b = 1; while (i < n) {
w = a + b;
a = b;
b = w;
i = i + 1;
} print(w, "\n");
}
}
case 7
{
push {
/* 12 factorial is 479001600 */
n = 12; result = 1; i = 1; while (i <= n) {
result = result * i;
i = i + 1;
} print(result);
}
}
}
end select
Push {
Form! 104, 60
{
/*
This is an integer ascii Mandelbrot generator
*/
left_edge= -420;
right_edge=300;
top_edge=300;
bottom_edge = -300;
x_step=7;
y_step=15;
max_iter=200;
y0 = top_edge;
while (y0 > bottom_edge) {
x0 = left_edge;
while (x0 < right_edge) {
y = 0;
x = 0;
the_char = ' ';
i = 0;
while (i < max_iter) {
x_x = (x * x) / 200;
y_y = (y * y) / 200;
if (x_x + y_y > 800 ) {
the_char = '0' + i;
if (i > 9) {
the_char = '@';
}
i = max_iter;
}
y = x * y / 100 + y0;
x = x_x - y_y + x0;
i = i + 1;
}
putc(the_char);
x0 = x0 + x_step;
}
putc('\n');
y0 = y0 - y_step;
}
}
}
Form ! 120, 60
Refresh
Refresh
Print "Lexical Analyzer" : Refresh
Print "Lexical Analyzer" : Refresh
lexical_analyzer
lexical_analyzer
if not islet then exitr
Print "Syntaxl Analyzer" : Refresh
Print "Syntax Analyzer" : Refresh
syntax_analyzer
syntax_analyzer
if not islet then exit
Print "Code Generator" : Refresh
Print "Code Generator" : Refresh
CodeGenerator
CodeGenerator
if not islet then exit
Virtual_Machine_Interpreter
if ask("Choose execution style", "Info", "Virtual Machine", "*M2000 Transpiler")=1 then
Virtual_Machine_Interpreter
else
Transpiler_to_M2000
end if
Print "done"
Set Fast 'restore speed setting
Set Fast 'restore speed setting
}
}
CompilerExample
CompilerExample


</lang>
</lang>