Παράδειγμα γραμμένο σε Μ2000. Ένας μίνι μεταφραστής της Lisp . Αυτόν έφτιαχνα πριν μερικές βδομάδες και μου ήρθε ιδέα να προχωρήσω τη γλώσσα, και πράγματι βρήκα πολλά και νέα πράγματα και μικροπροβλήματα που έλυσα.
Ανοίγουμε τον διερμηνευτή, γράφουμε Σ Α ώστε να ανοίξει η Συγγραφή (ο διορθωτής), με το τμήμα Α που θα βάλουμε το κώδικα παρακάτω και μας εμφανίζει αυτό (έχω αλλάξει σε 60 χαρακτήρες τη κονσόλα, ενώ στο πρόγραμμα γράφει 80)
Ο χρόνος εκτέλεσης δεν μας πειράζει εδώ. Χρησιμοποιώ Windows 7 σε VirtualBox σε Linux.
Εκτελούμε το παρακάτω δοκιμαστικό πρόγραμμα σε Mini Lisp, αν μια λέξη στην αρχή μιας λίστας ( ) δεν την γνωρίζει ο διερμηνευτής τότε την γυρνάει σαν αποτέλεσμα! Οι εντολές έχουν σημειωθεί με bold (με το χέρι...)
"hello there this is a string" ; this is going to result
(
write 1223345.788)(
defvar Hello "M2000")(
write Hello)
(
write 121 "Hello again...")
(
eq 7 7)(
write (
list 1 2 4 5))(
defvar A (
+ 7 9 13 1 5 7) ) (
write A (- A 10) (* A 3) (/ 5 2))
(
defvar (a b c d) 100)(
write a b c d)(
setq a (+ a 1))(
write '(a =) a)(
write (
car '(a b c))(
cdr '(a b c))) ; remark
(
write (
cons 'a '(b c)))(
write George 'Karras)(
write (
eq a 101))
(this is written as result)(
write '(This written before results) (
+ a 1))
(
quote (Hello 1 There a b)) '(Hello 2 There a b)(
eq 5 5)
(
loop (
write a) (
eq a 107) (
setq a (+ a 1))) (
write (
list 1 A (
+ A 1) 3 4 5))
(
write 1 A (
+ A 1) 3 4 5)
(
write (
If t 5 6)) (
write ok)
(
write (
If nil (
list 1 2 3) (
list 4 5 6)))
(
write (
If nil 5 6))
(
write (
If t (
list 1 2 3) (
list 4 5 6)))
Profiler
EntryLevel =0
Module forall {
Local MyAcc$
Document MyAcc$
Clear Buffer$ \\ through old object and get a new one / = is For append
Call Local Parser(level, &MyAcc$)
}
Module Comm {
Read proc$
Call ! Local forall
Local dummy$=paragraph$(MyAcc$,-1,-1)
Local a, d, acc
Long a, d=forward(MyAcc$, a) \\ there is a backward() function too
If d<>0 Then {
Select Case proc$
Case "+"
{ While a<>0 { acc+=Val(paragraph$(MyAcc$, (a))) }}
Case "-"
{ If a<>0 Then acc=Val(paragraph$(MyAcc$, (a)))
While a<>0 { acc-=Val(paragraph$(MyAcc$, (a))) }}
Case "*"
{ acc=1: While a<>0 { acc*=Val(paragraph$(MyAcc$, (a))) }}
Else ' Case "/"
{ acc=1
If a<>0 Then acc=Val(paragraph$(MyAcc$, (a)))
While a<>0 { acc/=Val(paragraph$(MyAcc$, (a))) }}
End Select
Accum$=Format$("{0}", acc)+nl$
}
}
Module CommWrite {
Call ! Local forall
Print "Lisp:";
Local i, f=Doc.Par(MyAcc$)
If f>1 Then {
f-- : If f>1 Then Print "(";
For i=1 to f
Print paragraph$(MyAcc$, i); : If i<f Then Print " ";
Next i
}
If f>1 Then { Print ")" } Else Print
}
Module LoopLisp {
Local dummy$ : Clear Buffer$
cnt++ : Local backhere=cnt
Local mylevel=level
{
Call Local Parser(myLevel, &Accum$)
dummy$=paragraph$(Accum$,-1,-1) \\ throw last nl$
dummy$= paragraph$(Accum$,-1,-1)
Accum$=nl$ \\ insert last nl$
If dummy$="t" Then Exit
cnt=backhere
level=mylevel
loop
}
}
Module MyList {
Local MyAcc$
Document MyAcc$ : Clear Buffer$
Call Local Parser(level, &MyAcc$)
Accum$="("+Trim$(Replace$(nl$," ", MyAcc$))+")"+nl$
}
Module Myif {
Call ! Local forall
If paragraph$(MyAcc$, 1)="t" Then {
Accum$=paragraph$(MyAcc$, 2)+nl$
} else {
Accum$=paragraph$(MyAcc$, 3)+nl$
}
}
Module DefVar {
Local MyAcc$, Iddoc$
Document MyAcc$, Iddoc$ : Clear Buffer$
cnt++
Call Local Parser(level, &Iddoc$, True)
Call Local Parser(level, &MyAcc$)
Local id$=paragraph$(Iddoc$, 1,-1) \\ third parameter If is -1 Then delete the paragraph
If id$="" Then Error "No name For variable"
While Doc.Par(iddoc$)>0 {
Call Local PrintNewLine "Var :"+id$
If not exist(Mem,id$) Then {
Append Mem, id$:=MyAcc$
} Else Error "Variable "+id$+" already defined"
id$=paragraph$(Iddoc$, 1,-1)
}
}
Module SetVar {
Local MyAcc$, Iddoc$
Document MyAcc$, Iddoc$ : Clear Buffer$
cnt++
Call Local Parser(level, &Iddoc$, True)
cnt--
Call Local Parser(level, &MyAcc$)
Local id$=paragraph$(Iddoc$, 1,-1) \\ third parameter If is -1 Then delete the paragraph
If id$="" Then Error "No name For variable"
While Doc.Par(iddoc$)>0 {
Call Local PrintNewLine "Var :"+id$
If exist(Mem,id$) Then {
Return Mem, id$:=MyAcc$
} Else Error "No such variable"
id$=paragraph$(Iddoc$, 1,-1)
}
}
Module Cons {
Call ! Local forall
Accum$=MyAcc$
}
Module Remark {
while cnt<=tmproof {
cnt++
If mid$(a$, cnt,2)=nl$ Then cnt++ : exit
}
}
Module String {
Local check=cnt
Clear Buffer$ : numeric=false
while cnt<=tmproof {
cnt++
If mid$(a$, cnt,1)=qu$ Then exit
}
if cnt-check>1 Then { Buffer$=Mid$(a$,check, cnt-check+1)} Else Buffer$={""}
}
Module PrintBuffer {
If Doc.Len(Buffer$)>0 Then {
If exist(Mem, Buffer$) and not GetQuote Then {
Accum$=Mem$(Buffer$)
} else Accum$=Buffer$+nl$
Call Local PrintNewLine Buffer$
Clear Buffer$ : prints++
}
}
Module PrintNewLine {
Read New What$
If mess Then Print what$
}
Module StopChar {
Read New val
com = val=1
level+=val
If GetQuote And level<=EntryLevel Then Call Local PrintBuffer : GetQuote=false: tmproof=cnt : Exit
If val<>0 Then Local oldprints=prints : prints=0
Call Local PrintBuffer
If profil=1 And val=-1 Then If prints=0 Then If oldprints=prints Then Buffer$="()" : Call Local PrintBuffer
profil=val
}
Module StopCharZero {
If GetQuote And level=EntryLevel Then Call Local PrintBuffer:GetQuote=false: tmproof=cnt : Exit
If com Then Call Local CheckCommand
If not com Then Call Local PrintBuffer : Exit
}
Module StorePeriod {
If Doc.Len(Buffer$)=0 Then Call Local StoreNumeric : Exit
If numeric Else Call Local StoreLabel : Exit \\ see Else, same as If not numeric Then
If Instr(Buffer$,".")>0 Then Call Local PrintBuffer
Call Local StoreNumeric
}
Module StoreSign {
If Doc.Len(Buffer$)>0 And numeric Then Call Local PrintBuffer : Call Local StoreNumeric : Exit
Call Local StoreNumeric \\ maybe numeric=False so this is like storelabel...
}
Module StoreNumeric {
If Doc.Len(Buffer$)=0 Then numeric=True
buffer$=one$
}
Module StoreLabel {
numeric=False
buffer$=one$
}
Module CheckCommand {
com=false
If GetQuote Then Exit
If numeric Then {
If Doc.Len(Buffer$)=1 Then If Instr("*/+-",Buffer$)>0 Then Exit
Exit
}
If Exist(commands, Buffer$) Then Inline "Call Local "+Eval$(commands)
}
Module Sym {
Read new w$
Call ! Local forall
If Evall(quote$(paragraph$(MyAcc$, 1) )+ w$+ Quote$(paragraph$(MyAcc$, 2) )) Then {
Accum$=t$ } Else Accum$=nil$
}
Module Quote {
Clear Buffer$
cnt++
Call Local Parser(level, &Accum$, True)
cnt--
}
Module Car {
Call ! Local forall
Accum$=paragraph$(MyAcc$, 1)+nl$
}
Module Cdr {
Call ! Local forall
Local drop$=paragraph$(MyAcc$, 1,-1)
Accum$=MyAcc$
}
Module Eq {
Call ! Local forall
If paragraph$(MyAcc$, 1)=paragraph$(MyAcc$, 2) Then {
Accum$=t$
} Else Accum$=nil$
}
Function Parser {
Read New EntryLevel, &Back$
Local GetQuote
Read ? GetQuote \\ this is an optional argument For Parser
Local tmproof=roof, Accum$
Document Accum$
{
If level<EntryLevel Then cnt-- : Back$=Accum$ : Exit
one$=mid$(a$,cnt,1)
\\ warning in each Case one command in one line or use { } For multiple commands/lines
\\ no empty lines allowed.
Select Case one$
Case qu$
Call Local String
Case ";" \\ remark
Call Local Remark
Case "'" \\ quote
Call Local Quote
Case "("
Call Local StopChar, 1 ''StopChar(1)
Case ")"
Call Local StopChar, -1 ''StopChar(-1)
Case " ", Ch10$, Ch13$ ' white space too
Call Local StopCharZero
Case "0" to "9"
Call Local StoreNumeric
Case "."
Call Local StorePeriod
Case "-", "+"
Call Local StoreNumeric
Else
Call Local StoreLabel
End Select
cnt++
If cnt<=tmproof Then Restart
Back$=Accum$
}
}
\\ Mini lisp ver 0.01
Clear \\ clear variables
Flush \\ empty stack
Form 80,48
Print "MiniLisp Ver 0.01"
Scroll Split Row \\ make this row start of scrolling screen
\\ A simple lisp interpreter written in M2000 by George Karras
\\For Documents "=" is For append text. Clear a$, give a new object Document.
Document a$={
"hello there this is a string" ; this is going to result
(write 1223345.788)(defvar Hello "M2000")(write Hello)
(write 121 "Hello again...")
(eq 7 7)(write (list 1 2 4 5))(defvar A (+ 7 9 13 1 5 7) ) (write A (- A 10) (* A 3) (/ 5 2))
(defvar (a b c d) 100)(write a b c d)(setq a (+ a 1))(write '(a =) a)(write (car '(a b c))(cdr '(a b c))) ; remark
(write (cons 'a '(b c)))(write George 'Karras)(write (eq a 101))
(this is written as result)(write '(This written before results) (+ a 1))
(quote (Hello 1 There a b)) '(Hello 2 There a b)(eq 5 5)
(loop (write a) (eq a 107) (setq a (+ a 1))) (write (list 1 A (+ A 1) 3 4 5))
(write 1 A (+ A 1) 3 4 5)
(write (If t 5 6)) (write ok)
(write (If nil (list 1 2 3) (list 4 5 6)))
(write (If nil 5 6))
(write (If t (list 1 2 3) (list 4 5 6)))
}
Print "Parse:";
Report a$
Print
nl$={
} \\ nl$ is new line
Let level=0, one$="", numeric=False, prints=0, com=False, profil=1, ch10$=chr$(10),ch13$=chr$(13), qu$=chr$(34)
Let mess=false '\\true \\ For messages
Let cnt=1, roof=len(a$), comlevel=-1
' no command
Document Buffer$, Result$
\\ Inventory is a hash table.
Inventory commands="+":="Comm {+}", "-":="Comm {-}" , "*":="Comm {*}", "/":="Comm {/}", "write":="CommWrite", "car":="Car", "cdr":="Cdr", "cons":="Cons", "defvar":="DefVar", "eq":="Eq", "setq":="SetVar", "quote":="Quote", "loop":="loopLisp",">":="Sym {>}" ,"<":="Sym {<}", ">=":="Sym{>=}","<=":="Sym{<=}", "<>":="Sym{<>}", "list":="MyList","If":="Myif"
Clear t$, nil$
\\ we want a paragraph only - with new line
Inventory mem \\ For variables
t$="t"+nl$
nil$="nil"+nl$
Append Mem, "t":=t$,"nil":=nil$
Try ok { Call Local Parser(level, &Result$)}
If Error or Not Ok Then Print Error$
If level>0 Then Error "more ("
If Not Mess Then {
Report 2, "Results"
Report Replace$(nl$," ", Result$)
}
Print Timecount
Το παραπάνω πρόγραμμα με Ρουτίνες αντί για τμήματα. Ο σκοπός εδώ είναι να μειωθεί η χρήση του stack του exe, και οι ρουτίνες στη Μ2000 χρησιμοποιούν δικό τους σωρό επιστροφής, και για το λόγο αυτό έχουν μεγάλο αριθμό για κλήσεις με αναδρομή (μπορεί κανείς να θέσει όριο 100000, και το βάζουμε μόνο για να πετύχουμε τα τυχόν λάθη στο κώδικα). Πρέπει όμως να μην κάνουμε κλήση ρουτίνας μέσα από μπλόκ { } γιατί τα μπλόκ καλούν συνάρτηση εσωτερικά άρα χρησιμοποιούν τον στάνταρ σωρό του exe. Όπου καλούμε την Parser() προσέχουμε να μην είναι μέσα σε μπλοκ. (αφήνουμε τα μπλοκ για άμεσες επαναλήψεις).
Επιπλέον στο πρόγραμμα μπήκε μια βελτιστοποίηση στον Parser με πρόσθετη Κατάσταση που σημαίνει ότι η αναζήτηση στη κατάσταση γίνεται άμεσα. Βέβαια δεν έχει μεγάλη διαφορά, γιατί ακόμα και ο χρόνος χρήσης του κλειδιού στην Κατάσταση σε σχέση με το κλειδί στην Select Case, μπορεί να μην είναι τόσο καλύτερος, ειδικά αν το Select Case είναι μικρό. Πάντως εδώ κερδίζουμε.
Τα goto, και τα Gosub (όλες οι ρουτίνες καλούνται με Gosub, απλά είναι προαιρετικό να το γράψουμε, εκτός και αν υπάρχει πίνακας με ίδιο όνομα, και έτσι φαίνονται οι ρουτίνες από τις παρενθέσεις, σαν συναρτήσεις χωρίς να επιστρέφουν τιμή). Οι ρουτίνες βλέπουν ότι βλέπουμε και στο τμήμα που τρέχουν, δηλαδή και τις άλλες ρουτίνες και τον εαυτό τους, και τις μεταβλητές του τμήματος.
X=10
Gosub Alfa()
Alfa() \\ same as Gosub Alfa()
List \\ here we see only one X=10, M not exist
Sub Alfa()
Local X=20, Μ=10
List \\ here we see X=10 and X=20 and M=10
End Sub
Υπάρχει και άλλος τύπος ρουτινών με ετικέτα με γράμματα ή αριθμό αλλά αυτές δεν καθορίζουν προσωρινό χώρο για μεταβλητές. Σε κάθε περίπτωση η έκδοση 8.4 καταγράφει τις θέσεις μετά την πρώτη αναζήτηση και στην δεύτερη τις βρίσκει από πίνακα κατακερματισμού (Hash Table), όπως βρίσκει και τα Τμήματα/Συναρτήσεις (παλαιότερα δεν γίνονταν αυτό).
Profiler
EntryLevel =0
\\ Mini lisp ver 0.02
Clear \\ clear variables
Flush \\ empty stack
Form 60,48
Print "MiniLisp Ver 0.02"
Scroll Split Row \\ make this row start of scrolling screen
\\ A simple lisp interpreter written in M2000 by George Karras
\\For Documents "=" is For append text. Clear a$, give a new object Document.
Document a$={
"hello there this is a string" ; this is going to result
(write 1223345.788)(defvar Hello "M2000")(write Hello)
(write 121 "Hello again...")
(eq 7 7)(write (list 1 2 4 5))(defvar A (+ 7 9 13 1 5 7) ) (write A (- A 10) (* A 3) (/ 5 2))
(defvar (a b c d) 100)(write a b c d)(setq a (+ a 1))(write '(a =) a)(write (car '(a b c))(cdr '(a b c))) ; remark
(write (cons 'a '(b c)))(write George 'Karras)(write (eq a 101))
(this is written as result)(write '(This written before results) (+ a 1))
(quote (Hello 1 There a b)) '(Hello 2 There a b)(eq 5 5)
(loop (write a) (eq a 107) (setq a (+ a 1))) (write (list 1 A (+ A 1) 3 4 5))
(write 1 A (+ A 1) 3 4 5)
(write (If t 5 6)) (write ok)
(write (If nil (list 1 2 3) (list 4 5 6)))
(write (If nil 5 6))
(write (If t (list 1 2 3) (list 4 5 6)))
}
Print "Parse:";
Report a$
Print
nl$={
} \\ nl$ is new line
Let level=0, one$="", numeric=False, prints=0, com=False, profil=1, ch10$=chr$(10),ch13$=chr$(13), qu$=chr$(34)
Let mess=false '\\true \\ For messages
Let cnt=1, roof=len(a$), comlevel=-1
Global Inventory TopGun = chr$(34):="String()", ";":="Remark()", "'":="Quote()", "(":="StopChar(1)", ")":="StopChar(-1)", " ":="StopCharZero()", Chr$(10):="StopCharZero()", Ch13$ :="StopCharZero()", ".":="StorePeriod()", "-":="StoreNumeric()", "+":="StoreNumeric()"
For i=0 to 9: Append TopGun, str$(i,"0"):="StoreNumeric()" : Next i
Document Buffer$, Result$
\\ Inventory is a hash table.
Inventory commands="+":="Comm({+})", "-":="Comm({-})" , "*":="Comm({*})", "/":="Comm({/})", "write":="CommWrite()", "car":="Car()", "cdr":="Cdr()", "cons":="Cons()", "defvar":="DefVar()", "eq":="Eq()", "setq":="SetVar()", "quote":="Quote()", "loop":="loopLisp()",">":="Sym({>})" ,"<":="Sym({<})", ">=":="Sym({>=})","<=":="Sym({<=})", "<>":="Sym({<>})", "list":="MyList()","If":="Myif()"
Clear t$, nil$
\\ we want a paragraph only - with new line
Inventory mem \\ For variables
t$="t"+nl$
nil$="nil"+nl$
Append Mem, "t":=t$,"nil":=nil$
Try ok { Parser(level, &Result$)}
If Error or Not Ok Then Print Error$
If level>0 Then Error "more ("
If Not Mess Then {
Report 2, "Results"
Report Replace$(nl$," ", Result$)
}
Print Timecount
Sub StoreNumeric()
If Doc.Len(Buffer$)=0 Then numeric=True
buffer$=one$
End Sub
Sub StoreLabel()
numeric=False
buffer$=one$
End Sub
Sub Comm()
Read proc$
Local MyAcc$
Document MyAcc$:Clear Buffer$:Parser(level, &MyAcc$)
Local dummy$=paragraph$(MyAcc$,-1,-1)
Local a, d, acc
Long a, d=forward(MyAcc$, a) \\ there is a backward() function too
If d<>0 Then {
Select Case proc$
Case "+"
{ While a<>0 { acc+=Val(paragraph$(MyAcc$, (a))) }}
Case "-"
{ If a<>0 Then acc=Val(paragraph$(MyAcc$, (a)))
While a<>0 { acc-=Val(paragraph$(MyAcc$, (a))) }}
Case "*"
{ acc=1: While a<>0 { acc*=Val(paragraph$(MyAcc$, (a))) }}
Else ' Case "/"
{ acc=1
If a<>0 Then acc=Val(paragraph$(MyAcc$, (a)))
While a<>0 { acc/=Val(paragraph$(MyAcc$, (a))) }}
End Select
Accum$=Format$("{0}", acc)+nl$
}
End Sub
Sub CommWrite()
Local MyAcc$
Document MyAcc$ : Clear Buffer$: Parser(level, &MyAcc$)
Print "Lisp:";
Local i, f=Doc.Par(MyAcc$)
If f>1 Then {
f-- : If f>1 Then Print "(";
For i=1 to f {
Print paragraph$(MyAcc$, i);
If i<f Then Print " ";
}
}
If f>1 Then { Print ")" } Else Print
End Sub
Sub LoopLisp()
Local dummy$ : Clear Buffer$
cnt++ : Local backhere=cnt
Local mylevel=level
loop1:
Parser(myLevel, &Accum$)
dummy$=paragraph$(Accum$,-1,-1) \\ throw last nl$
dummy$= paragraph$(Accum$,-1,-1)
Accum$=nl$ \\ insert last nl$
If dummy$="t" Then Exit Sub
cnt=backhere
level=mylevel
goto loop1
End Sub
Sub MyList()
Local MyAcc$
Document MyAcc$ : Clear Buffer$
Parser(level, &MyAcc$)
Accum$="("+Trim$(Replace$(nl$," ", MyAcc$))+")"+nl$
End Sub
Sub Myif()
Local MyAcc$
Document MyAcc$:Clear Buffer$:Parser(level, &MyAcc$)
If paragraph$(MyAcc$, 1)="t" Then {
Accum$=paragraph$(MyAcc$, 2)+nl$
} else {
Accum$=paragraph$(MyAcc$, 3)+nl$
}
End Sub
Sub DefVar()
Local MyAcc$, Iddoc$
Document MyAcc$, Iddoc$ : Clear Buffer$
cnt++ : Parser(level, &Iddoc$, True) : Parser(level, &MyAcc$)
Local id$=paragraph$(Iddoc$, 1,-1) \\ third parameter If is -1 Then delete the paragraph
If id$="" Then Error "No name For variable"
While Doc.Par(iddoc$)>0 {
PrintNewLine("Var :"+id$)
If not exist(Mem,id$) Then {
Append Mem, id$:=MyAcc$
} Else Error "Variable "+id$+" already defined"
id$=paragraph$(Iddoc$, 1,-1)
}
End Sub
Sub SetVar()
Local MyAcc$, Iddoc$
Document MyAcc$, Iddoc$ : Clear Buffer$
cnt++:Parser(level, &Iddoc$, True):cnt--:Parser(level, &MyAcc$)
Local id$=paragraph$(Iddoc$, 1,-1) \\ third parameter If is -1 Then delete the paragraph
If id$="" Then Error "No name For variable"
While Doc.Par(iddoc$)>0 {
PrintNewLine("Var :"+id$)
If exist(Mem,id$) Then {
Return Mem, id$:=MyAcc$
} Else Error "No such variable"
id$=paragraph$(Iddoc$, 1,-1)
}
End Sub
Sub Cons()
Local MyAcc$
Document MyAcc$: Clear Buffer$: Parser(level, &MyAcc$)
Accum$=MyAcc$
End Sub
Sub Remark()
while cnt<=tmproof {cnt++:If mid$(a$, cnt,2)=nl$ Then cnt++ : Exit
}
End Sub
Sub String()
Local check=cnt :Clear Buffer$ : numeric=false
while cnt<=tmproof {cnt++:If mid$(a$, cnt,1)=qu$ Then exit
}
if cnt-check>1 Then { Buffer$=Mid$(a$,check, cnt-check+1)} Else Buffer$={""}
End Sub
Sub PrintBuffer()
If Doc.Len(Buffer$)=0 Then Exit Sub
If exist(Mem, Buffer$) and not GetQuote Then {
Accum$=Mem$(Buffer$)
} else Accum$=Buffer$+nl$
PrintNewLine(Buffer$)
Clear Buffer$ : prints++
End Sub
Sub PrintNewLine(What$)
If mess Then Print what$
End Sub
Sub StopChar(val)
com = val=1
level+=val
If GetQuote And level<=EntryLevel Then PrintBuffer() : GetQuote=false: tmproof=cnt : Exit Sub
If val<>0 Then Local oldprints=prints : prints=0
PrintBuffer()
If profil=1 And val=-1 Then If prints=0 Then If oldprints=prints Then Buffer$="()" : PrintBuffer()
profil=val
End Sub
Sub StopCharZero()
If GetQuote And level=EntryLevel Then PrintBuffer():GetQuote=false: tmproof=cnt : Exit Sub
If com Then CheckCommand()
If not com Then PrintBuffer()
End Sub
Sub StorePeriod()
If Doc.Len(Buffer$)=0 Then StoreNumeric() : Exit Sub
If numeric Else StoreLabel() : Exit Sub \\ see Else, same as If not numeric Then
If Instr(Buffer$,".")>0 Then PrintBuffer()
StoreNumeric()
End Sub
Sub StoreSign()
If Doc.Len(Buffer$)>0 And numeric Then PrintBuffer() : StoreNumeric() : Exit Sub
StoreNumeric() \\ maybe numeric=False so this is like storelabel...
End Sub
Sub CheckCommand()
com=false
If Not GetQuote Then If Exist(commands, Buffer$) Then { Inline Eval$(commands)}
End Sub
Sub Sym(w$)
Local MyAcc$
Document MyAcc$:Clear Buffer$:Parser(level, &MyAcc$)
If Evall(quote$(paragraph$(MyAcc$, 1) )+ w$+ Quote$(paragraph$(MyAcc$, 2) )) Then {
Accum$=t$ } Else Accum$=nil$
End Sub
Sub Quote()
Clear Buffer$: cnt++: Parser(level, &Accum$, True): cnt--
End Sub
Sub Car()
Local MyAcc$
Document MyAcc$:Clear Buffer$:Parser(level, &MyAcc$)
Accum$=paragraph$(MyAcc$, 1)+nl$
End Sub
Sub Cdr()
Local MyAcc$
Document MyAcc$:Clear Buffer$:Parser(level, &MyAcc$)
Local drop$=paragraph$(MyAcc$, 1,-1)
Accum$=MyAcc$
End Sub
Sub Eq()
Local MyAcc$
Document MyAcc$:Clear Buffer$:Parser(level, &MyAcc$)
If paragraph$(MyAcc$, 1)=paragraph$(MyAcc$, 2) Then {
Accum$=t$
} Else Accum$=nil$
End SUb
Sub Parser(EntryLevel, &Back$)
Local GetQuote
Read ? GetQuote \\ this is an optional argument For Parser
Local tmproof=roof, Accum$
Document Accum$
100 If level<EntryLevel Then cnt-- : Back$=Accum$ : Exit Sub
one$=mid$(a$,cnt,1)
If exist(TopGun, one$) then 200
StoreLabel()
cnt++ :If cnt<=tmproof then 100
Back$=Accum$
Exit Sub
200 inline Eval$(TopGun)
cnt++ :If cnt<=tmproof then 100
Back$=Accum$
End Sub
H Sub Parser μπορούσε να δοθεί έτσι, χωρίς τη χρήση της κατάστασης TopGun (λίστα με Hash table) η οποία όμως είναι κατι τις πιο αργή. Το βάζω εδώ για να κάνετε δοκιμή!
Προσοχή: Στις εξόδους από Sub χρειάζεται το Exit Sub, ενώ σε block τo απλό Exit. Στα μπλοκ ο κώδικας ανήκει σε μια συνάρτηση που όταν πάρει το Exit απλά επιστρέφει, ενώ το Exit Sub τραβάει την επιστροφή από τον ειδικό σωρό. Αν το ξεχάσουμε τότε στο επόμενο End Sub θα γυρίσουμε "αλλού για αλλού".
Sub Parser(EntryLevel, &Back$)
Local GetQuote
Read ? GetQuote \\ this is an optional argument For Parser
Local tmproof=roof, Accum$
Document Accum$
100 If level<EntryLevel Then cnt-- : Back$=Accum$ : Exit Sub
one$=mid$(a$,cnt,1)
\\ warning in each Case one command in one line or use { } For multiple commands/lines
\\ no empty lines allowed.
Select Case one$
Case qu$
String()
Case ";" \\ remark
Remark()
Case "'" \\ quote
Quote()
Case "("
StopChar(1)
Case ")"
StopChar(-1)
Case " ", Ch10$, Ch13$ ' white space too
StopCharZero()
Case "0" to "9"
StoreNumeric()
Case "."
StorePeriod()
Case "-", "+"
StoreNumeric()
Else
StoreLabel()
End Select
cnt++
if cnt<=tmproof then 100
Back$=Accum$
End Sub