Upload
irsyad-lubis
View
193
Download
0
Tags:
Embed Size (px)
DESCRIPTION
VendisScript's emulator implementation in Python. Describes how to implement a simple scripting language such as VendisScript in Python.
Citation preview
Implementasi VendisScriptdi Python
Oleh: Irsyad Asyhari Lubis
Apa itu VendisScript?
is a scripting language
A scripting language or script language is a programming language that supports the writing of scripts, programs written for a special runtime environment that can interpret and automate the execution of tasks which could alternatively be
executed one-by-one by a human operator.
Sumber: http://en.wikipedia.org/wiki/Scripting_language
Typically, a scripting language is characterized by the following properties:
● Ease of use. ● OS facilities - especially filesystem and related,
built in with easy interfaces. ● Interpreted from source code - to give the
fastest turnaround from script to execution. ● Relatively loose structure.
Sumber: http://en.wikipedia.org/wiki/Scripting_language
Typically, a scripting language is characterized by the following properties:
● Ease of use. ● OS facilities - especially filesystem and related,
built in with easy interfaces. ● Interpreted from source code - to give the
fastest turnaround from script to execution. ● Relatively loose structure.
Sumber: http://en.wikipedia.org/wiki/Scripting_language
is interpreted scripting language
An interpreted language is a programming language that avoids explicit program compilation. The interpreter executes the program source code directly, statement by statement, as a processor or scripting engine does. This can be contrasted
with compiled language programs, which the user must explicitly translate into a lower-level machine
language executable.
Sumber: http://en.wikipedia.org/wiki/Interpreted_language
is embedded, interpreted scripting language
sorry, no reference this time...
used in Android based mobile sales and distribution software
why not Python?
compared to Python, VendisScript is...
simple
in term of implementation
too simple...
no optimization
no optimization (yet)
but, there is one reason to rule them all
size
it does matter!
less why...
more how...
now we are talking about implementation
in Python
note that...
scripting language on top of Python is generally not necessary
unless it is DSL
since this is only emulator...
Apa itu VendisScript?
JSON + Lisp
JSON
{ "firstName": "John", "lastName": "Smith", "age": 25, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": 10021 }, "phoneNumbers": [ { "type": "home", "number": "212 5551234" }, { "type": "fax", "number": "646 5554567" } ]}
Sumber: http://en.wikipedia.org/wiki/JSON
Lisp
(defun factorial (n) (if (<= n 1) 1 (* n (factorial ( n 1)))))
Sumber: https://en.wikipedia.org/wiki/Lisp_(programming_language)
Basic syntax
[
{
"d": "Gratis 5 produk A untuk setiap pembelian 100 produk A.",
"i": ["and",
["has", "11112313", "@products"],
[">=",
["bulkqty", ["take", "11112313", "@products"]],
100.0]],
"o": ["setq", "bonuses",
["addbonus",
["bonusnew", "11112313",
["floatp", ["*", ["intp",
["/", ["bulkqty", ["take", "11112313", "@products"]], 100.0]], 5]],
"PCS"],
"@bonuses"]]
}]
[
{
"d": "Gratis 5 produk A untuk setiap pembelian 100 produk A.",
"i": ["and",
["has", "11112313", "@products"],
[">=",
["bulkqty", ["take", "11112313", "@products"]],
100.0]],
"o": ["setq", "bonuses",
["addbonus",
["bonusnew", "11112313",
["floatp", ["*", ["intp",
["/", ["bulkqty", ["take", "11112313", "@products"]], 100.0]], 5]],
"PCS"],
"@bonuses"]]
}]
{
"d": "Test factorial.",
"i": true,
"o": ["prog",
["setq", "fac",
["lambda", ["x"],
["cond",
["<=", "@x", 1], 1,
true, ["*", ["fac", ["", "@x", 1]], "@x"]]]],
["fac", 3]]}
{
"d": "Test factorial.",
"i": true,
"o": ["prog",
["setq", "fac",
["lambda", ["x"],
["cond",
["<=", "@x", 1], 1,
true, ["*", ["fac", ["", "@x", 1]], "@x"]]]],
["fac", 3]]}
def fac(x):if x <= 1:
return 1else:
return x * fac(x 1)
[{
“d”: @description(str),“i”: @input(JSON),“o”: @output(JSON)
},...
]
Demo
Compiler Stack
SourceCode Lexer Parser AST
TargetCode
Grammar
prog : ('[' item (',' item)* ']' | '[' ']') EOF;
item : '{' '"d"' ':' QUOTED_IDENTIFIER ',' '"i"' ':' expr ',' '"o"' ':' expr '}';
expr : | lambda_expr | let_expr | setq_expr | prog_expr | cond_expr | apply_expr | QUOTED_IDENTIFIER | VAR_ACCESS | '#nil' | INT | FLOAT | 'true' | 'false' ;
lambda_expr : '[' '"lambda"' ',' '[' params ']' ',' body ']';
params : QUOTED_IDENTIFIER (',' QUOTED_IDENTIFIER)*;
body : expr ;
let_expr : '[' '"let"' ',' '[' init_list* ']' ',' body ']';
setq_expr : '[' '"setq"' ',' QUOTED_IDENTIFIER ',' expr ']';
init_list : '[' QUOTED_IDENTIFIER ',' expr ']';
prog_expr : '[' '"prog"' (',' expr)+ ']';
cond_expr : '[' '"cond"' (',' cond_and_expr)+ ']';
cond_and_expr : expr ',' expr;
apply_expr : '[' QUOTED_IDENTIFIER (',' expr)* ']';
VAR_ACCESS : '"@' IDENTIFIER '"';
QUOTED_IDENTIFIER : '"' IDENTIFIER '"';
INT : ''? INT_WITHOUT_PREFIX;FLOAT : ''? INT_WITHOUT_PREFIX '.' [09]* EXP?;
WS : [ \t\n\r]+ > skip;
fragmentIDENTIFIER : (ESC | ~('"'|'\\'))*;
fragmentINT_WITHOUT_PREFIX : '0'|[19][09]*;
fragmentEXP : [Ee][+|]? INT_WITHOUT_PREFIX;
fragmentESC : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\\'|UNICODE);
fragmentUNICODE : 'u' HEX HEX HEX HEX;
fragmentHEX : [09afAF];
Lexer
JSON's lexer + parser
Parser
AST
1 + (2 * 3)
["+", 1, ["*", 2, 3]]
+
1 *
2 3
class Expression(object):def __init__(self, args=None):
self.args = args if args else []
def evaluate(self, env):pass
+
1 *
2 3
AddExpression
MulExpressionIntExpression
IntExpression IntExpression
class IntExpression(Expression): def __init__(self, value): self.value = value
def evaluate(self, env): return self.value
class AddExpression(BasicMathExpression): def evaluate(self, env): return self._evaluate(env, 'Add', lambda x, y: x + y)
class MulExpression(BasicMathExpression): def evaluate(self, env): return self._evaluate(env, 'Mul', lambda x, y: x * y)
Tipe data
✔ Integer → 1 0 1 2✔ Float → 2.0 1.2 2.3✔ String → "This is a string"✔ List → ["list", 1, 2, 3.0,
"This is a string", ["list", 2, 3]]
✔ Empty list → "#nil"✔ Boolean → true false✔ Function✔ Product
Cons
["cons", 1, ["cons", 2, ["cons", 3, "#nil"]]]
["list", 1, 2, 3]
( head tail )
( head ( head tail ) )
( head ( head ( head tail ) ) )
( head ( head ( head nil ) ) )
class Cons(object): def __init__(self, head=None, tail=None): self._head = head self._tail = tail
@property def head(self): return self._head
@property def tail(self): return self._tail
@staticmethod def from_seq(seq): if not seq: return None
head = Cons(seq[0]) current = head
for item in seq[1:]: next_cons = Cons(item) current._tail = next_cons current = next_cons
return head
def to_list(self): result = []
self.each(result.append)
return result
def each(self, f): f(self.head)
tail = self.tail while tail != None: if isinstance(tail, Cons): f(tail.head) tail = tail.tail else: f(tail) tail = None
def map(self, f):pass
def filter(self, f):pass
def reduce(self, f, *args):pass
Scope
Dynamic Scope vs Lexical Scope
Dynamic Scope vs Lexical Scope
two constructs to introduce a variable
["setq", "variable1", 100]
["let", [["variable1", 100]]["print", "@variable1"]]
function's paramenters also introduce local variables
{
"d": "Test factorial.",
"i": true,
"o": ["prog",
["setq", "fac",
["lambda", ["x"],
["cond",
["<=", "@x", 1], 1,
true, ["*", ["fac", ["", "@x", 1]], "@x"]]]],
["fac", 3]]}
class BonusEnvironment(dict): def __init__(self, parent=None): self._parent = parent
def __getitem__(self, key): if key not in self and self._parent: return self._parent[key] else: return super(BonusEnvironment, self).__getitem__(key)
def set_global(self, key, value): # by convention, global environment is whose parent # is None. if not self._parent: self[key] = value else: self._parent.set_global(key, value)
class GetValExpression(Expression): def evaluate(self, env): name = self._evaluated_args(env, 'GetVal', ((str, unicode),)) try: return env[name] except KeyError: # search in builtin functions try: builtin_class_name = builtin_expressions[name] builtin_class = globals()[builtin_class_name] builtin_instance = builtin_class() return BuiltInFunction(body=builtin_instance) except KeyError: return None
Functions
63 builtin functions
["setq", "fib",["lambda", ["n"],
["cond",["<", "@n", 2], 1,true, ["+", ["fib", ["", "@n", 1]],
["fib", ["", "@n", 2]]]]]]
class LambdaExpression(Expression): def __init__(self, params, body): self.params = params self.body = body
def evaluate(self, env): return Function(self.params, self.body)
class ApplyExpression(Expression): def __init__(self, funcName, args): self.funcName = funcName self.args = args
def evaluate(self, env): func = env[self.funcName] if isinstance(func, Function): return func.apply(env, self.args) else: raise ExpressionException('Apply: Cannot find function with name {0}'.format(self.funcName))
how to add new builtin function?
it should be easy, right?
Demo
Regrets
should not use None as value
class NilType(object):def __nonzero__(self):return False
Nil = NilType()
Thank you!!!