skx / critical Goto Github PK
View Code? Open in Web Editor NEWA simple/minimal TCL interpreter, written in golang
License: GNU General Public License v2.0
A simple/minimal TCL interpreter, written in golang
License: GNU General Public License v2.0
Right now our parser/lexer doesn't do anything special with comments.
The end result is the following lines don't behave as you'd expect:
# This is a comment
// This is also a comment
What they actually do is invoke the TCL command "#", or "//", and pass the whole line as arguments. In practice that works out - the comment
primitive just does nothing and all appears well.
However there is one problem, these two lines cause syntax errors:
#Note the lack of space between "#" and "Note"?
//Same here.
When executed you'll see this:
frodo ~/critical $ ./critical bad.tcl
Error running program:unknown command '#Note':{token{Type:IDENT Literal:#Note} [token{Type:IDENT Literal:the} ...
Or this:
frodo ~/critical $ ./critical bad.tcl
Error running program:unknown command '//Same':{token{Type:IDENT Literal://Same} [token{Type:IDENT Literal:here.}]}
Here we're obviously trying to run the word/proc/command "//Same", and "#Note". Neither exist, so they throw an error.
Fix this by just swallowing the comments in the lexer, and getting rid of the comment
proc in the interpreter.
I hacked together a simple FORTH:
And demonstrated that here:
Do something similar here, to show how to embed.
The following code runs:
proc foo {} {
if { 1 } {
return "steve"
} else {
return "bar"
}
puts "I'm still here ..?"
}
foo 1
Obviously foo
should take zero arguments, but calling like that fails:
$ ./critical a.tcl
Error running program:function argument mismatch, foo takes 1 arguments, 0 supplied
This is the first of two issues with that same piece of code. See also #16.
https://gitlab.com/cznic/tcl/-/blob/master/go.mod
Would be interesting what this can do
We don't yet have 100% test-coverage, so that should be done first, but then we should have fuzz-tests for both packages.
Need to check with a real TCL to see if this is expected, but the following program surprised me by returning 5
and not printing anything:
5
puts "OK"
I suspect that we're seeing the 5, considering it a literal, and returning from our evaluation loop.
How to source a tcl file?
Error running program:unknown command 'source':{token{Type:IDENT Literal:source} [...]
We don't have for
yet, so I think those are the only places these make sense.
(Much like adding return
#9 we'll just define a special kind of Error
and use that to flag the loop handling in the builtin_while.go
file.)
I had to rewrite @antirez's code from this:
proc fib {x} {
if {<= $x 1} {
return 1
} else {
+ [fib [- $x 1]] [fib [- $x 2]]
}
}
To this:
proc fib {x} {
if { expr $x <= 1 } {
return 1
} else {
return [expr [fib [expr $x - 1]] + [fib [expr $x - 2]]]
}
}
i.e. Replace { <= $x 1}
with { expr $x <= 1}
, because I don't have the prefix-forms present.
But of course we could add them:
proc + {a b} { expr $a + $b}
..
proc <= {a b} { expr $a <= $b}
Create an embedded STDLIB to do that, and define repeat
as well as other functions.
I wrote a naive benchmark test:
//
// Source code of the script we're going to run
//
src := `return 34`
//
// return values
//
var err error
var out string
// create
i := New(src)
//
// Now run the thing in a loop
//
b.ResetTimer()
for n := 0; n < b.N; n++ {
// execute
out, err = i.Evaluate()
}
b.StopTimer()
It doesn't work. Because we essentially run:
The first call to the Evaluate
method invokes the parser, which then consumes the program. That means future executions do nothing.
We should move the Parse step to the constructor to allow the interpreter to re-run the same script multiple times.
This is related to #5, but I ran out of time last night.
Something like this:
proc square {x} { * $x $x }
square 7
This might mean we need to add continue
, break
, and return
. We'll cross those bridges when we come to them..
This is almost certainly going to turn out to be related to expansion.
Consider this program, to print a string five times:
repeat 5 { puts " * " }
Works just fine:
$ ./critical newline.tcl
*
*
*
*
*
Result: *
Now remove the spaces:
repeat 5 { puts "*" }
And suddenly:
frodo ~/critical $ ./critical err.tcl
*
*
*
*
*
Error running program:function argument mismatch, * takes 2 arguments, 0 supplied
Replacing *
with +
, or similar, produces the same error message.
This should return the contents, between them, unprocessed
So "if { $a < 4 }
" will return the literal text $a < 4
when this is hit.
If I can fix this then the complexity of if
and while
will go away, and that means that we can add proc
to define user-functions.
It won't be a massive change, but I guess adding a parser/
package would be the sanest way to go.
number
, string
, and list
list
will hold Objects so it can contain strings, integers, or other lists.environment
package so the variables are now map[string] *Object
Annoyingly at that point we'll almost certainly need to change our function signatures for HostFunction
to read:
function func(i *Interpreter, args []*Object) (Object, error)
Once that is done we'll have:
list
, returning those types.The other complication will be that we'll need to validate types on our primitives, not just argument-counts.
e.g. exit
will need to ensure it has "string|number". Similarly the return value of interpreter.Evaluate
and interpreter.Eval
will need to be *Object,error
rather than string,error
So significant churn, but the actual steps are pretty simple.
We don't have any CI process setup against this repository; add it.
At the least we're seeing failures now:
frodo ~/Repos/github.com/skx/tcl $ ~/go/lint
interpreter/interpreter.go:310:10: if block ends with a return statement, so drop this else and outdent its block
interpreter/interpreter.go:316:10: if block ends with a return statement, so drop this else and outdent its block
interpreter/interpreter.go:322:10: if block ends with a return statement, so drop this else and outdent its block
interpreter/interpreter.go:328:10: if block ends with a return statement, so drop this else and outdent its block
interpreter/interpreter.go:369:9: if block ends with a return statement, so drop this else and outdent its block
interpreter/interpreter.go:469:3: this value of err is never used (SA4006)
lexer/lexer.go:29:2: field decimal is unused (U1000)
frodo ~/Repos/github.com/skx/tcl $
Since adding floating-point support, in #20, the test-case coverage has slipped.
(Mostly because we try to return an int/float as appropriate. Handle both cases and ensure our test coverage is back to 100%.
The following code, as used in #15, has a second failure:
proc foo {} {
if { 1 } {
puts "steve"
return "steve"
} else {
puts "bar"
return "bar"
}
puts "I'm still here ..?"
}
foo 1
Running this shows:
$ ./critical a.tcl
I'm still here ..?
Result:I'm still here ..?
The issue here is that either branch should have resulted in the return
being executed - but it wasn't. So the execution hit the puts at the foot of the code.
TLDR; return
is broken inside if
. (Possibly while
and for
too?)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.