任务:
您必须创建一个可以解析编程语言摘要的解释器。该语言不必很复杂,但必须包含以下语法元素:
- 分配和读取变量的能力(可以很简单
a
-z
是预制变量) - If语句(不需要elseif和else)
- 循环(计数为任意数字,不需要用户访问计数器)
- 带有变量(加,减,乘,除,大于/小于,等于)的简单数学
- 打印报表
规则:
任务:
您必须创建一个可以解析编程语言摘要的解释器。该语言不必很复杂,但必须包含以下语法元素:
a
- z
是预制变量)规则:
Answers:
99瓶啤酒计划:
many amaze 99 time
such scare bottles-of-beer
such scream on-the-wall
many despair 13 time
such fail take-one-down-pass-it-around
wow
so amaze
so scare
so scream
so despair!
so amaze
so scare
so despair!
much amaze
so fail
so despair!
so amaze
so scare
so scream
so despair!
so despair!
very amaze
wow
PHP解释器:
<?php
$input=fopen('php://stdin', 'r');
//pre-process input
$input=preg_replace("/ +/", " ", $input); //replace any multiple spaces by a single space
//split into instructions by newlines
$instructions=explode("\n", $input);
$loopstartpoint= -1;
$variables=array();
$activevariable="";
for($instrpointer=0; $instrpointer<count($instructions); $instrpointer++)
{
$tokens=explode(" ", $instructions[$instrpointer]);
switch($tokens[0])
{
case "wow":
if($loopstartpoint<0)
{
$loopstartpoint=$instrpointer+1;
}
else
{
if($variables[ $activevariable ])
{
$instrpointer=$loopstartpoint;
}
else
{
$loopstartpoint= -1;
}
}
break;
case "so":
if(substr($tokens[1], -1)=="!")
{
echo chr($variables[ substr($tokens[1], 0, -1) ]);
}
else
{
echo $variables[ $tokens[1] ];
echo " ";
}
break;
case "very":
$activevariable=$tokens[1];
break;
case "much":
if(!isset($variables[ $tokens[1] ]))
$variables[ $tokens[1] ]=0;
if(count($tokens)==2)
{
$variables[ $tokens[1] ]--;
}
else
{
for($loop=0;$loop<$tokens[2];$loop++)
{
$variables[ $tokens[1] ]--;
}
}
$activevariable=$tokens[1];
break;
case "many":
if(!isset($variables[ $tokens[1] ]))
$variables[ $tokens[1] ]=0;
if(count($tokens)==2)
{
$variables[ $tokens[1] ]++;
}
else
{
for($loop=0;$loop<$tokens[2];$loop++)
{
$variables[ $tokens[1] ]++;
}
}
$activevariable=$tokens[1];
break;
case "such":
$variables[ $tokens[1] ]=$tokens[2];
$activevariable=$tokens[1];
break;
}
}
?>
目前的语法:
wow - start and end loops, end of loop checks if active variable is 0 and loops if not
so without ! - print variable's value
so with ! - print variable's ASCII character
much - decrement this variable
many - increment this variable
such - set variable
very - make variable active
x time - does previous statement x times
Variables are initially 0.
在这里尝试。
欢迎提出任何改进建议。
注意:我发布此答案后,规范从“创建解析器”更改为“创建解释器”。这个答案是也可以解析源代码的编译器。
该名称是Back上的双关语,与基于栈的著名语言和Brain相反表示其深奥的本质。它看起来有点像BrainFuck(虽然不是),但是它的编译器在BrainFuck上运行,并且编译后的目标代码最终成为BrainBuck二进制文件。
语言:* ==销毁它的参数
"constant"
打印常量#
将堆栈顶部打印为数字>
复制堆栈的顶部<num>
推常数 <num>
作为值堆栈顶部<
移除堆栈顶部-
从第二高位减去最高位*+
将最高处添加到第二最高处*!
不切换正/零*[ ... ]
一段时间的堆栈顶部不为零,与BrainFuck非常相似BrainBack中的99瓶正确歌词的啤酒:
100
[
1 -
> ! [ < 0 0 "No more" ] < [ # 0 ] <
" bottle"
> 1 - [ < 0 "s" ] <
" of beer on the wall, "
> ! [ < 0 0 "no more" ] < [ # 0 ] <
" bottle"
> 1 - [ < 0 "s" ] <
" of beer.
"
> ! [ < 0 0 "Go to the store and buy some more, " ] <
[ "Take one down and pass it around, " 0 ] <
> ! [ < 1 0 0 "99" ] < [ > 1 - > ! [ < < 1 0 0 "no more" ] < [ # 1 - 0 ] ] <
" bottle"
[ < 0 "s" ] <
" of beer on the wall.
"
]
BrainBack编译器,用Extended BrainFuck编写
;;; Macros
;; utility function that substracts
;; ^2 from ^0 without reduceing ^2
;; below zero. ^1 needs to be zero
{substract
(<<[->]>[<]>-)
}
;; Main macro is the main program and
;; has the overal structure of the program.
;; every macro here is define in order below.
{main
:i
:wrk
:tmp
:else
:sub
$i+(
; switch ( $wrk ) cases '"#><-+![]9;' using $tmp,$else
$tmp+++++(-$wrk------)+$wrk---; !
($wrk-; "
($wrk-; #
($wrk--------; +
($wrk--; -
($wrk--- $tmp- $else 9+ &substract $tmp+ $wrk; 0-9
($wrk--; ;
($wrk-; <
($wrk--; >
($tmp++++(-$wrk------)+$wrk+; [
($wrk--; ]
(#(-) $tmp(-) no matches)
$tmp (- #match 'cl' &close )
) $tmp (- #match 'op' &open )
) $tmp (- #match 'gt' &dup )
) $tmp (- #match 'lt' &slash )
) $tmp ( #match 'c' &comment )
) $tmp (- #match 0 to 9 &read_number )
) $tmp (- #match 'minus' &sub )
) $tmp (- #match 'plus' &add )
) $tmp (- #match '#' &print_number )
) $tmp (- #match '"' &print_string )
) $tmp (- #match 'not' ¬ )
$i(-)$tmp#,+(-(-$wrk+$i+)))
10+.
}
;; implements close bracket
{close
|" close"(-)
$i.
}
;; implements open bracket
{open
|" open"(-)
$i.
}
;; implements dup/>
{dup
|"dup [->>+<<]>>[-<+<+>>]<
"
(-)
}
;; implements slash/<
{slash
|"slash [-]<
"
(-)
}
;; implements comment
{comment
[,10-]
}
;; implements read_number/<number>
;; makes code that if run makes
;; the constant
{read_number
;TODO: compiler_read_constant_number
$wrk|"number"(-)
# $wrk 6+ (- $i 8-)
~"+>"<.(-)
$i+(-(-$wrk.)
#$else, $tmp 6+ (- $else 8-)
$else(-$tmp+$i+)
$sub 9+ &substract
$else+
$tmp((-) $i(-) $else-)
$else(-|"[->++++++++++<]>[-<+>]<"(-)$i+)
)
$wrk(-)
|"
"(-)
}
;; implements sub/-
{sub
|"sub [-<->]<
"
(-)
}
;; implements add/+
{add
|"#add [-<+>]<
"
(-)
}
;; implements print_number/#
{print_number
|"print [->+<]>[-<+>>+<]>
[>++++++++++<
[->-[>+>>]>[+[-<+>]>+>>]<<<<<]
+>[-]>[-<<+>>]>[-<<+>>]<<]
+<[>-<[<]]>[>]
<[>++++++[-<++++++++>]<-.[-]<]<
"(-)
}
;; implements print_string/"..."
;; this outputs EBF code making the
;; object code EBF
{print_string
|"print >|"(-)
$i(-$wrk+$else+)
$wrk($tmp(-$wrk+)$wrk.,$else(-$tmp+$wrk-$i+)$i(-$else+))
$tmp(-)$else.(-)|"[-]<
"(-)
}
;; implements not/!
;; creates code that negates top of stack
{not
|"not >+<[[-]>-]>[<+>->]<<
"(-)
}
&main
编译BrainBack:
bf ebf.bf < BrainBack.ebf > BrainBack.bf
编译BrainBack程序:
bf BrainBack.bf < 99.bb > 99.ebf # compile from bb to ebf
bf ebf.bf < 99.ebf > 99.bf # compile from ebf to bf
运行二进制文件:
bf 99.bf
在这里,我使用大多数debian发行版中都可用的bf。beef
其他也可以使用。EBF编译器BrainBack及其目标代码都变得完全兼容BrainFuck二进制文件。
它可能应该扩展为以ascii格式打印一个单元格.
,能够读取一个字节,
并进行各种swap
操作才更有用。为了在BrainBack中创建BrainBack编译器或解释器,这是绝对必要的。
我将大部分时间都花在PHP脚本上,这给我带来了一个问题:为什么我被迫使用$
变量名?€
是我的当地货币,所以让我们使用它吧!由于€在许多国家/地区使用,因此我使用了一些来自欧盟语言的词作为关键字。
€beers gleich 99
€bottles gleich bottles of beer
€bottles_on_the_wall gleich bottles of beer on the wall
mientras €beers topogleich 3
afficher €beers €bottles_on_the_wall
afficher , €beers €bottles
afficher . NETHERLANDS
odejmowanie €beers
afficher Take one down and pass it around, €beers
afficher €bottles_on_the_wall
afficher . NETHERLANDS NETHERLANDS
sartneim
afficher 2 bottles of beer on the wall, 2 bottles of beer. NETHERLANDS
afficher Take one down and pass it around, 1 bottle of beer on the wall.
afficher NETHERLANDS NETHERLANDS
afficher 1 bottle of beer on the wall, 1 bottle of beer. NETHERLANDS
afficher Take one down and pass it around, no more bottles of beer on the wall.
afficher NETHERLANDS NETHERLANDS
afficher No more bottles of beer on the wall, no more bottles of beer. NETHERLANDS
afficher Go to the store and buy some more, 99 bottles of beer on the wall.
afficher NETHERLANDS NETHERLANDS
关键字:
gleich
是等于在德国mientras
是同时在西班牙topo
是更大的葡萄牙语(更新:它应该是MAIOR相反,由于daHugLenny的提示)odejmowanie
被减去波兰afficher
被打印在法国nl
有时会调用换行符,而TLD是NETHERLANDS
is nl
,所以我定义了一个常量NETHERLANDS
来显示换行符由于没有if
关键字,我作弊了一点,我选择直接打印最后两行。
解释器仅执行脚本即可显示99瓶啤酒。
# -*- coding: utf-8 -*-
# @see http://stackoverflow.com/questions/12655836/writing-an-xml-file-that-contains-a-euro-symbol-in-python-using-xml-etree/12655861#12655861
# = gleich (german)
# while mientras (spanish)
# > topo (portuguese) (it should be "maior" instead)
# subtract odejmowanie (polish
# print afficher (french)
# newline NETHERLANDS
import sys, codecs
class euro:
symbols = {}
sign = u'€'
def executeLine(self, line):
s = line.split(' ')
if s[0] == 'afficher':
buffer = []
for a in s[1:]:
if (a == ''):
continue
elif (a[0] == self.sign):
buffer.append(str(self.getSymbol(a)))
elif (a == 'NETHERLANDS'):
buffer.append("\n")
else :
buffer.append(a)
sys.stdout.write(' '.join(buffer))
# @see http://stackoverflow.com/questions/4499073/printing-without-newline-print-a-prints-a-space-how-to-remove/4499172#4499172
elif s[0] == 'odejmowanie':
self.setSymbol(s[1], (int(self.getSymbol(s[1])) - 1))
elif (len(s) >= 3) and (s[1] == 'gleich'):
self.setSymbol(s[0], (' ').join(s[2:]))
def executeBlock(self, lines, statement):
while (self.getStatement(statement)):
for line in lines:
self.executeLine(line)
def getStatement(self, statement):
if (statement[1] == 'topogleich'):
return self.getSymbol(statement[0]) >= int(statement[2])
def setSymbol(self, name, value):
name = self.withoutEuro(name)
self.symbols[name] = value
def getSymbol(self, name):
#~ print symbols, withoutEuro(name)
name = self.withoutEuro(name)
if name in self.symbols:
value = self.symbols[name]
return value
else :
print "\n-----\n",'Error: "', name, '"is not in', self.symbols, '-----'
#~ sys.exit()
def withoutEuro(self, string):
return(string.replace(self.sign, ''))
def parseFile(self, f):
linesStack = []
for line in codecs.open(f, 'r', 'utf-8'):
line = line.replace('\n', '').replace('\t', '')
s = line.split(' ')
if (len(s) == 1) & (s[0] == '') :
continue
if (s[0] == 'mientras'):
statement = s[1:]
linesStack.append(line)
elif (s[0] == 'sartneim'):
linesStack.append(line)
self.executeBlock(linesStack, statement)
linesStack = []
statement = ''
elif (len(linesStack) > 0):
linesStack.append(line)
else:
self.executeLine(line)
euro = euro()
euro.parseFile(sys.argv[1])
要运行它,请保存两个文件,然后使用.eu
脚本作为参数运行Python文件:
python euro.py euro.eu
topo
是顶在葡萄牙
1Lang是一种功能性前缀语言,如LISP或Scheme,但没有括号,因此当删除所有不必要的空白时,它很难阅读。由于所有函数和运算符均采用已知数量的参数,因此可以删除括号。
需要大括号来分隔函数主体和条件结果,以及由语句列表组成的备用代码块。
在LISP中,阶乘可以这样定义:
(defun fact (x) (if (< x 2) 1 (* x (fact (- x 1))) ) )
在1Lang中,这将是
@Fx{ ? < x 2 {1} {* x F -x1} }
可以减少到
@Fx{?<x2{1}{*xF-x1}}
1Lang目前不支持任何副作用。
1Lang用bash编写,因此它当前具有一些bash限制,例如整数范围。
a-z are variables. Variable are either integers, strings, or lists.
注意:列表尚未完全实现。
A-Z are functions
整数是bash整数(我认为最大为-2 ^ 32至2 ^ 31-1)。负数不能直接使用。要输入负数,请从零中减去。例如。-5将被输入为-05。此限制是因为1Lang正在开发中,并且此应用程序不需要负数。我正在考虑将〜用作一元负运算符,它允许将-5输入为〜5。
空格用于描述整数。例如。+2 3
: means assign eg. :c34 to assign 34 to c
+-*/% are binary integer operators eg. +12 34
&|^ are binary bit-wise operators
! is unary boolean not
~ is unary one's complement
? is a if-then-else function-like operator. eg. ?=x3{*xx}{0} is x=3 return x*x else 0
+ is also a binary string concatenation operator eg. +99" bottles"
* is also a string repetition operator eg. *5" hello" or *" hello"5
@ defines a function eg. @Fx{?<x1{1}{*xF-x1}}
函数参数名称可能会使调用者变量过载。在函数内分配的所有变量都是局部变量。
不需要打印(尽管它可能有用),因为像LISP一样,每个语句都返回一个值,并打印最后返回的值。
eg. +2 3 prints 5
没有括号的前缀表示法的意外行为是字符串串联实际上很容易编写。假设您要串联"a" " quick" " brown" " fox"
,您可能会这样写:
+++"a"" quick"" brown"" fox"
但是,这是一种更具可读性和更少错误的方法:
+"a"+" quick"+" brown"" fox" (Note missing + between last terms)
要么
+"a"+" quick"+" brown"+" fox"""
99瓶啤酒代码:
:b" of beer"
:w" on the wall"
:t"Take one down and pass it around, "
:s"Go to the store and buy some more, "
:c", "
:n".\n"
@Bx{?=x0{+"No more bottles"b}{+x+" bottle"+?=x1{""}{"s"}b}}
@Fx{?=x0{+B0+w+c+B0+n+s+B99+wn}{+Bx+w+c+Bx+n+t+B-x1+w+n+"\n"F-x1}}
F99
函数B根据x返回“不再装瓶”或“ 1瓶”或“瓶”。
函数F返回正常的经文或最终的经文。通过用-x1递归调用F,将普通经文与后继经文连接在一起。当x为0时,F返回最终句。
这会生成(对于F5,意味着从5瓶啤酒开始...):
> F5
5 bottles of beer on the wall, 5 bottles of beer.
Take one down and pass it around, 4 bottles of beer on the wall.
4 bottles of beer on the wall, 4 bottles of beer.
Take one down and pass it around, 3 bottles of beer on the wall.
3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.
2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.
1 bottle of beer on the wall, 1 bottle of beer.
Take one down and pass it around, No more bottles of beer on the wall.
No more bottles of beer on the wall, No more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
<End>
#!/bin/bash
LC_ALL=C # else [a-z] and [A-Z] misbehave
# functions return result on stdout
# functions have an environment
# Requirements:
# * minimise size
# -> eliminate delimiters
# -> single letter variables and functions
# -> no precidence
# -> no overloading
# *
# string "text with \characters as per printf"
# numbers 123
# functions F3
# Built-ins +-*/%^ &|~ ! etc.
# assignment :v12 :v"string"
log(){ local m="${l:p}" m="${m//[$NL]/\n}" v="${FUNCNAME[1]}"; echo "$v: l=[${l//[$NL]/\n}] ch=[${ch/[$NL]/\n}] next=[$m]" >&2; }
logr(){ local m="${l:p}" m="${m//[$NL]/\n}" v="${FUNCNAME[1]}"; echo "$v: l=[${l//[$NL]/\n}] ch=[${ch/[$NL]/\n}] next=[$m] ret=[${ret//[$NL]/\n}]" >&2; }
logv(){ local v="${FUNCNAME[1]}"; echo "$v: ret=[${ret//[$NL]/\n}]" >&2; }
logm(){ local m="$1" v="${FUNCNAME[1]}"; echo "$v: ${m//[$NL]/\n} in [${read//[$NL]/\n}]." >&2; }
msg(){ echo -En "$1" >&2; }
msn(){ echo -E "$1" >&2; }
# ==========
# Line layer
# ==========
declare l
readline(){ read -rp"1lang> " l; }
#==================
# Environment Layer
#==================
declare -A v t # variables and variable type
declare ret typ # all bash function return these values
# assign = : var expression
assign(){
local var
readch
var && var=$ret || { logm "ERROR: variable name expected" ; return 1; }
exp || { logm "ERROR: value or expression expected"; return 1; }
v["$var"]="$ret"
t["$var"]="$typ"
}
# get variable value
get(){
local var
var && var=$ret || { logm "ERROR: variable name expected"; return 1; }
ret=${v["$var"]}
typ=${t["$var"]}
}
declare -A func fpar
declare -iA fnum # functions
# define = @ F param* { body }
define(){
local fn par body
readch
fn && fn=$ret || { logm "ERROR: function name expected"; return 1; }
fpar[$fn]= # zero parameters
fnum[$fn]= # zero parameter counter
while var;do # read parameters
fpar[$fn]+=$ret
fnum[$fn]+=1 # cound parameters
done
# get body but remove block delimiters
skip "{" "}" && body="${ret:1: -1}" || { logm "ERROR: function body expected"; return 1; }
readch # skip }
func[$fn]="$body" # store function body
ret="@$fn${fpar[$fn]}{$body}"
typ='f'
}
apply(){
local fn=$ch n c s; local -i N q
readch
N=${fnum[$fn]} # number of parameters
n=${fpar[$fn]} # parameters
s=${func[$fn]} # function body
c=
for((q=0; q<N; q++)){
exp || { logm "ERROR: value expected"; return 1; }
c+="v[${n:q:1}]=\"$ret\"; " # add value to script
c+="t[${n:q:1}]=\"$typ\"; " # add type to script
}
# parse function in a subshell and echo result and type back
# subshell means all variable changes in function are local
c+="parse <<<'$s'; echo -E \"\$typ\$ret\"" # combine type and value
ret=
typ=
ret="$( eval "$c" )" || { logm "ERROR: function application failed"; return 1; }
typ="${ret::1}" # extract type
ret="${ret:1}" # get actual return value
}
# bash oddities:
# [[ 1 -eq 1 ]] -> 0 or success
# [[ 1 -eq 2 ]] -> 1 or failed
# x=1\<2 -> a=1 (true)
# x=1\<1 -> a=0 (false)
# ((1==1)) -> 0 or success
# ((1==2)) -> 1 or failed
# declare -i a; a=1==1 -> a=1 (true)
# declare -i a; a=1==2 -> a=0 (false)
binary(){
local -i iret; local op=$ch a b at bt
readch
exp && { a="$ret"; at=$typ; } || { logm "ERROR: initial expression expected"; return 1; }
exp && { b="$ret"; bt=$typ; } || { logm "ERROR: second expression expected" ; return 1; }
ret=
typ=
case "$at$bt" in
nn) # num op num
case "$op" in
[\*]) iret=a*b;;
[\^]) iret=a**b;;
[\+]) iret=a+b;;
[\-]) iret=a-b;;
[\/]) [[ b -ne 0 ]] && { iret=a/b; } || { logm "ERROR: division by 0" ; return 1; };;
[\%]) [[ b -ne 0 ]] && { iret=a%b; } || { logm "ERROR: modulo division by 0"; return 1; };;
[\&]) iret=a\&b;;
[\|]) iret=a\|b;;
[\#]) iret=a\^b;;
[\=]) iret=a==b;;
[\<]) iret=a\<b;;
[\>]) iret=a\>b;;
esac
ret=$iret
typ='n';; # result is always a decimal number
ss) # string op string
case "$op" in
# [\*]) arith=a*b;; # combine?
# [\#]) arith=${}a**b; type='s';;
[\+]) ret="$a$b"; typ='s';; # concatenate
[\-]) ret="${a//$b}"; typ='s';; # remove substrings
[\=]) [[ $a = $b ]]; ret=$?; typ='n';;
[\<]) [[ $a < $b ]]; ret=$?; typ='n';;
[\>]) [[ $a > $b ]]; ret=$?; typ='n';;
esac;;
ns) # num op string =3"hello" ="hello"3 ="3"3 =3"4"
case "$op" in
[\+]) ret="$a$b"; typ='s';; # concatenate
[\*]) ret=$(eval echo \"\${b[0]\"{1..$a}\"}\"); typ='s';; # repeat b a times
[\=]) ((${#b}==a)); ret=$?; typ='n';; # length b is a
# [\<]) [[ $a < $b ]]; arith=$?; typ='n';;
# [\>]) [[ $a > $b ]]; arith=$?; typ='n';;
esac;;
sn) # string op num *"hello"3 ="3"3 =3"4"
case "$op" in
[\+]) ret="$a$b"; typ='s';; # concatenate
[\*]) ret=$(eval echo \"\${a[0]\"{1..$b}\"}\"); typ='s';; # repeat a b times
[\=]) ((${#a}==b)); ret=$?; typ='n';; # length a is b
# [\<]) [[ $a < $b ]]; arith=$?; typ='n';;
# [\>]) [[ $a > $b ]]; arith=$?; typ='n';;
esac;;
*) logm "ERROR: undefined operation [$op] for [$a] [$at] and [$b] [$bt]"; return 1;
esac
return 0
}
# FIXME: string ops?
unary(){
local -i iret; local op="$ch"
readch
exp || { logm "ERROR: expression expected"; return 1; }
case "$op" in
[\!]) iret=\!ret;;
[\~]) iret=\~ret;;
esac
ret=$iret
typ='n' # result is always a decimal number
}
#==============
# Control Layer
#==============
# iff = ? boolean { consequence block } { alternative block }
# ?<1 2{+4 5}{+1 2}
iff(){
local -i c; local iff ift
readch
exp && c=$ret || { logm "ERROR: value or expression expected"; return 1; }
[[ c -eq 1 ]] && { # true so do consequence
ws
block && { iff="$ret"; ift="$typ"; } || { logm "ERROR: consequence block error"; return 1; }
ws
skip "{" "}" || { logm "ERROR: alternate block expected"; return 1; }
ret="$iff"
typ="$ift"
} || {
ws
skip "{" "}" || { logm "ERROR: consequence block expected"; return 1; }
ws
block || { logm "ERROR: alternate block error"; return 1; }
}
}
#==============
# Symbols Layer
#==============
# fn = [A-Z]
fn(){
# FIXME: make evalu?
[[ $ch = [A-Z] ]] || return 1
ret=$ch
typ='c'
readch
}
# var = [a-z]
var(){
# FIXME: make evalu?
[[ $ch = [a-z] ]] || return 1
ret=$ch
typ='c'
readch
}
# list = ( token* )
# FIXME: not finished and no operators support lists
list(){
local list=$ch prev
readch
while [[ $ch != ')' ]];do
exp || { logm "ERROR: expression expected"; return 1; }
case $typ in
[n]) list+=" $ret";;
[s]) list+="$ret";;
[l]) list+="$ret";;
esac
ws
done
ret="$list$ch"
readch
typ='l'
return 0
}
#============
# Token Layer
#============
# char = ' echoch
#echoch = \ {special echo escape character} | {char}
char(){
readch
case "$ch" in
[\\]) escch || { logm "ERROR: escape character expected"; return 1; };;
?) ret="$ch"; readch
esac
typ='c'
}
# escaped characters are a pain
# use read with -r to read in verbatim - no escaping
# use echo -E to write out verbatim (except \\ may be processed)
declare escchS
declare ECHO='abefnrtv'
# double \\ for a \
escch(){
local ESC="$ch"
readch # skip \
case "$ch" in
[$ECHO]) printf -v ret "%b" "$ESC$ch"; readch;;
[\\]) ret="\\"; readch;;
[\"]) ret="\""; readch;;
[0-7]) onum && { printf -v ret "%b" "$ESC$ret" ; } || { logm "ERROR: octal number expected"; return 1; };;
[xU]) readch; hnum && { printf -v ret "%b" "${ESC}x$ret"; } || { logm "ERROR: hex number expected" ; return 1; };;
?) ret="$ch"
[[ $escchS ]] || {
tidyReadCh
logm "WARNING: only octal, hex, unicode, and [$ECHO\\\"] characters need to be escaped with '$ESC'"
logm "WARNING: [$ch] in [$l] does not need to be escaped"
escchS="OFF"
}
readch
esac
typ='c'
}
# num = digit digit*
# onum = odigit odigit*
# onum = hdigit hdigit*
num(){ local num; num=$ch; readch; while digit;do num+=$ret; done; ret=$num; typ='n'; }
onum(){ local num; num=$ch; readch; while odigit;do num+=$ret; done; ret=$num; typ='n'; }
hnum(){ local num; num=$ch; readch; while hdigit;do num+=$ret; done; ret=$num; typ='n'; }
# digit = [0-9]
# odigit = [0-7]
# odigit = [0-9a-fA-F]
digit(){ [[ $ch == [0-9] ]] || { ret=-1; return 1; }; ret=$ch; typ='s'; readch; }
odigit(){ [[ $ch == [0-7] ]] || { ret=-1; return 1; }; ret=$ch; typ='s'; readch; }
hdigit(){ [[ $ch == [0-9a-fA-F] ]] || { ret=-1; return 1; }; ret=$ch; typ='s'; readch; }
# string = " char* "
# char = escch | {any character}
string(){
skip "\"" "\"" || { logm "ERROR: quoted string expected"; return 1; }
ret="${ret:1: -1}"
typ='s'
return 0
}
# ==========
# Char layer
# ==========
declare ch read
declare -i p L COUNT
readch(){
if [[ p -eq L ]]; then # need more code
readline || { ch=; p=L=0; l="EOF"; return 1; }
l+=$NL;
p=0
L=${#l}
fi
# FIXME: remove once eady - prevents bash consuming all memory
COUNT+=1
((COUNT>100000)) && { logm "FAILSAFE: too many charcters read"; return 1; }
ch="${l:p:1}"
read+="$ch"
p+=1 # queue next character
}
# skip = SS content* ES
# content = ch | escch | skip(SS ES)
# string = " ch* "
skip(){
local s="$1" e="$2" b="$ch"
typ='z' # code fragment
[[ $ch != $s ]] && return # nothing to skip
readch
while [[ -n $ch ]];do
case "$ch" in
$e) b+="$e" ; readch; ret="$b"; return 0;;
$s) skip "$s" "$e"; b+="$ret";;
[\\]) escch ; b+="$ret";;
[\"]) skip "\"" "\""; b+="$ret";;
?) b+="$ch" ; readch
esac
done
ret="$b"
logm "ERROR: unexpected EOF"
exit 1
}
# FIXME: still required?
shopt -s extglob
shopt -u nocasematch
declare NL; printf -v NL "%b" "\n" # echo $NL | hexdump -C
declare WS; printf -v WS "%b" " \n\t\r" # define whitespace
# FIXME: should it set ret and typ?
ws(){ while [[ $ch == [$WS] ]];do readch; done; } # skip any WS
#=====
# eval
#=====
# exp = [0-9] num
# | " string "
# | : assignment
# | @ function definition
# | [-+*/%^] binary operation
# | [&|#<>=] boolean operation
# | [!~] unary operation
# | [A-Z] function application
# | [a-z] variable
# | ? if expression
# | { expression* } block expression
# | ( expression* ) list of expressions
# spare prefix characters [ '$[]_\;, ]
# [v head of list
# ]v tail of list
exp(){
ws
case "$ch" in
[0-9]) num || { logm "ERROR: number expected" ; return 1; };;
# [\']) char || { logm "ERROR: char expected" ; return 1; };;
[\"]) string || { logm "ERROR: string expected" ; return 1; };;
[\:]) assign || { logm "ERROR: assignment expected" ; return 1; };;
[\@]) define || { logm "ERROR: function definition expected" ; return 1; };;
[-+*/%^]) binary || { logm "ERROR: binary expression expected" ; return 1; };;
[\&\|#\<\>=]) binary || { logm "ERROR: binary expression expected" ; return 1; };;
[\!~]) unary || { logm "ERROR: unary expression expected" ; return 1; };;
[A-Z]) apply || { logm "ERROR: function failed" ; return 1; };;
[a-z]) get || { logm "ERROR: variable name expected" ; return 1; };;
[\?]) iff || { logm "ERROR: boolean expression expected" ; return 1; };;
[\{]) block || { logm "ERROR: code block expected" ; return 1; };;
[\(]) list || { logm "ERROR: list expected" ; return 1; };;
'') ret=; logm "ERROR: unexpected EOF" ; return 1;;
*) ret="$ch" ; return 1;;
esac
return 0
}
# block = { code }
block(){
readch # skip {
while [[ $ch != "}" ]];do
exp || {
tidyReadCh
logm "WARNING: ignoring previous error or unknown symbol [$ch]"
[[ errors+=1 -gt 5 ]] && { logm "ERROR: exiting due to too many warnings"; exit 1; }
}
ws
done
readch # skip }
return 0
}
#=====
# repl
#=====
# pass an expression on stdin- not used withing same ebvironment - called by apply
parse(){
p=L # force readline
ch=
read=
readch # clears ch
while [[ $ch && $ch != '.' ]];do
exp || { logm "ERROR: expression expected"; return 1; }
read=$ch
ws
done
# last expression is returned as result
}
tidyReadCh(){
tidyRead
ch="${ch//[$NL]/\n}"
}
tidyRead(){
read="${read//[$NL]}"
}
# repl = eval* EOF
# eval = evalu | readch
repl(){
readch
while [[ $ch && $ch != '.' ]];do
exp && {
tidyRead
msn "> $read" # echo line except for WS
# echo -E "$ret [$typ]"
echo -E "$ret"
read=$ch
} || {
tidyReadCh
msn "> $read"
logm "WARNING: ignoring previous error or unknown symbol [$ch]"
read=
readch
[[ errors+=1 -gt 5 ]] && { logm "ERROR: exiting due to too many warnings"; exit 1; }
}
ws
done
msn "<End>"
}
#=====
# test
#=====
# FIXME: negative numbers
msn "1Lang"
repl <<<'
:b" of beer"
:w" on the wall"
:t"Take one down and pass it around, "
:s"Go to the store and buy some more, "
:c", "
:n".\n"
@Bx{?=x0{+"No more bottles"b}{+x+" bottle"+?=x1{""}{"s"}b}}
@Fx{?=x0{+B0+w+c+B0+n+s+B99+wn}{+Bx+w+c+Bx+n+t+B-x1+w+n+"\n"F-x1}}
F99
'
@Mfxy{fxy}M+3 4
但随后您需要加入函数和变量名称空间。它花了一段时间才计算出99杯啤酒:p
cons
您就可以map
M\x{*x2}C1C2C3C4/ => (2 4 6 8)
我不知道为什么我要在Windows批处理中回答这么多的难题,出于某种病态的原因,我认为我很喜欢:P无论如何,这类似于我前一段时间从事的工作,一种基本的语言通过也用Windows Batch编写的脚本翻译为Windows Batch。这并不特别令人惊奇,但可以。
# Initialize variables
bottles ~ 99
# You can't directly compare a literal value
zero ~ 0
# This makes a point 'loop' that can be jumped to or used as a subroutine
mark loop
write $ bottles
# You only need quotes when you have leading or trailing spaces
print ~ " bottles of beer on the wall,"
write $ bottles
print ~ " bottles of beer."
print ~ Take one down and pass it around,
bottles @ bottles-1
if
bottles equ zero
jump none
endif
write $ bottles
print ~ " bottles of beer on the wall."
print ~
jump loop
mark none
print ~ no more bottles of beer on the wall.
print ~
print ~ No more bottles of beer on the wall,
print ~ No more bottles of beer.
print ~ Go to the store and buy some more,
print ~ 99 bottles of beer on the wall.
每行仅识别三个标记,以空格分隔。
#是评论。
在大多数情况下,需要一个值时,$
第二个令牌中的a表示第三个令牌应视为变量名,而a则~
表示文字值。一般说明采用以下形式<instruction> [$~] <name>
。设置变量采用相同的形式,但是只要无法识别就可以实现。
定义的命令:
print
和write
两个写输出,但write
不添加一个换行符。需要$或〜。mark
创建一个可以跳转到子例程或称为子例程的点。jump
等效于批量转到(或与此相关的任何语言)。proc
调用子程序。相当于call :label
。return
从子例程返回。如果不在其中,将退出程序。if
有条件的指示。从下一行以形式进行比较<var1> <operator> <var2>
。运算符与的运算符相同if
,即。EQU, NEQ, LSS, LEQ, GTR, GEQ
。仅当比较为真时,才在其后执行指令。endif
结束if语句。cat
连接两个变量。cat a b
将ab的值存储在a中。如果找不到这些命令,则将第一个标记用作变量名称,将表达式视为变量分配。$
的~
行为与中的相同print
,但也有一个@
标识符。这会将最后一个标记视为传递给的数学表达式set /a
。它包括大多数运营商。如果找不到这三个标识符,则为语法错误,解释器退出。
解释器实际上将代码转换为Windows批处理,将其放置在临时文件中并执行它。虽然它可以识别Half语言的语法错误,但生成的批处理脚本可能会引起问题,尤其是括号,竖线等特殊字符时。
@echo off
REM Half Interpreter / Translator
if exist ~~.bat del ~~.bat
if not exist "%1" call :error "File not found: '%1'"
set error=
setlocal enabledelayedexpansion
call :parse "%1" 1>~~.bat
if exist ~~.bat if not "error"=="" ~~.bat 2>nul
goto :eof
:parse
set ifstate=0
echo @echo off
echo setlocal
echo setlocal enabledelayedexpansion
for /f "eol=# tokens=1,2* delims= " %%a in (%~1) do (
if "!ifstate!"=="1" (
if /i not "%%b"=="equ" if /i not "%%b"=="neq" if /i not "%%b"=="lss" if /i not "%%b"=="leq" if /i not "%%b"=="gtr" if /i not "%%b"=="geq" call :error "Unknown comparator: '%%b'"
echo if "^!%%a^!" %%b "^!%%c^!" ^(
set ifstate=0
) else (
if "%%a"=="print" (
if "%%b"=="$" (
echo echo.^^!%%c^^!
) else if "%%b"=="~" (
echo echo.%%~c
) else call :error "Unknown identifier for print: '%%b'"
) else if "%%a"=="write" (
if "%%b"=="$" (
echo echo^|set/p="^!%%c^!"
) else if "%%b"=="~" (
echo echo^|set/p="%%~c"
) else call :error "Unknown identifier for write: '%%b'"
) else if "%%a"=="mark" (
if not "%%c"=="" call :error "Unexpected token: %%c"
echo :%%b
) else if "%%a"=="jump" (
if not "%%c"=="" call :error "Unexpected token: %%c"
echo goto :%%b
) else if "%%a"=="proc" (
if not "%%c"=="" call :error "Unexpected token: %%c"
echo call :%%b
) else if "%%a"=="return" (
if not "%%c"=="" call :error "Unexpected tokens: %%b %%c"
if not "%%b"=="" call :error "Unexpected token: %%b"
echo goto :eof
) else if "%%a"=="if" (
if not "%%c"=="" call :error "Unexpected tokens: %%b %%c"
if not "%%b"=="" call :error "Unexpected token: %%b"
set ifstate=1
) else if "%%a"=="endif" (
if not "%%c"=="" call :error "Unexpected tokens: %%b %%c"
if not "%%b"=="" call :error "Unexpected token: %%b"
echo ^)
) else if "%%a"=="cat" (
echo set "%%b=^!%%b^!^!%%c^!"
) else (
if "%%b"=="$" (
echo set "%%a=!%%c!"
) else if "%%b"=="~" (
echo set "%%a=%%~c"
) else if "%%b"=="@" (
echo set/a"%%a=%%c"
) else call :error "Unknown tokens '%%a %%b %%c'"
)
)
)
echo endlocal
goto :eof
:error
echo.Parse Error: %~1 1>&2
set error=1
goto :eof
分配变量,否则分配条件块和其他一些加减运算。
乳胶锉 lex.l
%{
#include <stdio.h>
#include <stdlib.h>
%}
var [A-Za-z][A-Za-z0-9]*
digit [0-9]+
comment \*\*[A-Za-z0-9\*\/\+\-\(\)\"\' \t;:=]*\n
%%
print {return(PRINT);}
save {return(SAVE);}
{digit} {yylval=atoi(yytext);return(DIGIT);}
{var} {yylval=strdup(yytext);return(VAR);}
\* {return(M_SIGN);}
\/ {return(D_SIGN);}
\+ {return(A_SIGN);}
\- {return(S_SIGN);}
\( {return(L_BRACE);}
\) {return(R_BRACE);}
= {return(E_SIGN);}
; {return(S_COLON);}
: {return(COMMA);}
\n {return (NW_LINE);}
[ \t] /*skip*/;
{comment} /*skip*/;
%%
解析器文件 com.y
%{
#include <ctype.h>
#include <stdio.h>
FILE *save_p;
int new_line=1,stack_top=0,trigger=1;
void value_store(int);
int check_srore(char name_var[],int);
void error(int);
struct store
{
int var_value;
char var_name[10];
}info[10];
%}
%token PRINT SAVE S_COLON L_BRACE R_BRACE DIGIT VAR COMMA NW_LINE
%left A_SIGN S_SIGN
%left D_SIGN M_SIGN
%right E_SIGN
%%
commands :
| commands command
;
command : expers
| print
| save
| NW_LINE{new_line++;}
;
save : SAVE expr etest {fprintf(save_p,"%d\n",$2);}
;
expers : store_val equal expr etest{value_store($3);}
;
print : PRINT expr etest {printf("%d\n",$2);}
;
etest : S_COLON
| DIGIT {error(0);}|PRINT{error(0);}|SAVE{error(0);}
| VAR{error(0);}|COMMA{error(0);}
;
store_val : VAR {check_store($1,0);}
;
expr : expr A_SIGN expr { $$ = $1 + $3; }
| expr S_SIGN expr { $$ = $1 - $3; }
| expr M_SIGN expr { $$ = $1 * $3; }
| expr D_SIGN expr { $$ = $1 / $3; }
| L_BRACE expr R_BRACE { $$ = $2; }
| DIGIT
| retriv_var
;
equal : E_SIGN
;
retriv_var : VAR { $$=check_store($1,1); }
;
%%
#include "lex.yy.c"
void error(int temp)
{
char *err[]={
"Statement Missing\n",
"Compund Statement Missing\n",
"Variable need a value\n",
"Invalid Argument\n"
};
printf("In line no.%d:\t%s",new_line,err[temp]);
exit(1);
}
void value_store(int store_val)
{
stack_top--;
info[stack_top++].var_value = store_val;
}
int check_store(char name_var[],int status)
{
int temp = 0;
do{
if(strcmp(info[temp].var_name,name_var)==0)
{
trigger=0;
if(status)
{
trigger=1;
return (info[temp].var_value);
}
}
temp++;
} while(temp<stack_top);
if(trigger)
{
if(status)
{
trigger=1;
error(2);
}
else
strcpy(info[stack_top++].var_name,name_var);
}
else trigger=1;
}
int yyerror(const char *str)
{
fprintf(stderr,"error: %s\n",str);
}
main(int argc, char *argv[])
{
if(argc != 3)
{
error(3);
}
yyin = fopen(argv[1],"r");
save_p = fopen(argv[2],"w");
yyparse();
fclose(yyin);
fclose(yyout);
}
编译
跑
编译器in.txt ou.txt
输入文件
a = 3 +(4 * 7)-9; 打印 c = a + 45;打印c;
**这是注释保存c;
**将c保存在文件print c *(a + 32)中;
输出文件67
有关如何运行此代码的说明,请查看我的其他答案:https : //codegolf.stackexchange.com/a/19935/13186
bottles of beer on the wall, @ bottles of beer.
Take one down and pass it around, @ bottles of beer on the wall.
@ bottle of beer on the wall.
1 bottle of beer on the wall, 1 bottle of beer.
Take one down and pass it around, no more bottles of beer on the wall.
@@@@@@@@@@@@@@
#9.{
!#48.+
!<#57.<#0.^<!<#57.<#1.^<!<#56.<#64.^<
!<#56.<#0.^<!<#56.<#1.^<!<#55.<#64.^<
!<#55.<#0.^<!<#55.<#1.^<!<#54.<#64.^<
!<#54.<#0.^<!<#54.<#1.^<!<#53.<#64.^<
!<#53.<#0.^<!<#53.<#1.^<!<#52.<#64.^<
!<#52.<#0.^<!<#52.<#1.^<!<#51.<#64.^<
!<#51.<#0.^<!<#51.<#1.^<!<#50.<#64.^<
!<#50.<#0.^<!<#50.<#1.^<!<#49.<#64.^<
!<#49.<#0.^<!<#49.<#1.^<!<#48.<#64.^<
!<#48.<#0.^<!<#48.<#1.^<!#1.-<#57.<#64.^<
_
?}
#57.<#0.^<#57.<#1.^<!<#56.<#64.^<
#56.<#0.^<#56.<#1.^<!<#55.<#64.^<
#55.<#0.^<#55.<#1.^<!<#54.<#64.^<
#54.<#0.^<#54.<#1.^<!<#53.<#64.^<
#53.<#0.^<#53.<#1.^<!<#52.<#64.^<
#52.<#0.^<#52.<#1.^<!<#51.<#64.^<
#51.<#0.^<#51.<#1.^<!<#50.<#64.^<
#50.<#0.^<#50.<#1.^<!<#49.<
#94.^<
$
99ISC使用任意大小的面向整数的内存。内存由非负整数索引。内存中的所有值均使用其地址初始化。例如。在运行时,地址0包含值0,而地址9包含值9。
99ISC有两条指令。第一版在Wall例程中打印出99瓶啤酒。它的语法是一行,如下所示。执行从程序的下一行继续。
.
第二条指令是“如果不等于零,则减去并分支”指令。它的语法是一行,如下所示。
x y z
x
是要操作y
的数字的地址,是要减去的数字的地址,以及z
是如果减法的结果不为零的下一行要执行的代码。否则,执行从下一行开始。
“减零和分支如果不为零”指令的存在使99ISC成为OISC(一个指令集计算机),因此图灵完成了。
这是一个程序,它擦除内存中的前10个值,然后在Wall例程中打印99瓶啤酒。
1 1 0
2 2 0
3 3 0
4 4 0
5 5 0
6 6 0
7 7 0
8 8 0
9 9 0
.
这是一个使用Python的99ISC解释器。
def interpret(filename):
mem = range(0, 10)
print mem
with open(filename) as f:
lines = f.readlines()
ptr = 0
while ptr < len(lines):
line = lines[ptr]
if line.strip() == ".":
for i in range(99,0,-1):
text = str(i) + " bottles of beer on the wall, " + str(i) + " bottles of beer.\nTake one down and pass it around, " + str(i-1) + " bottles of beer on the wall.\n\n"
print text.replace("0", "No more")
else:
toks = map(int, line.split())
mem[toks[0]] = (mem[toks[0]] - mem[toks[1]]) & 0xFF
if mem[toks[0]] != 0:
ptr = toks[2]
else:
ptr += 1
我给你:
该语法采用BASIC和汇编语言。它有四个语句:set
,print
,jump
(无条件跳转),和jumpif
(条件跳转)。每个语句之前必须有一个行号。支持的数据类型是整数和字符串。
解释器本身可以在Github(sisi.py)的Python 3中找到。还有“ 99瓶啤酒”程序,但是我将在这里重现该程序:
10 set x 99
20 set bottles " bottles "
100 set line x + bottles
110 set line line + "of beer on the wall, "
120 set line line + x
130 set line line + bottles
135 set line line + "of beer."
140 print line
200 set x x - 1
210 set none x = 0
220 jumpif none 400
230 set multiple x > 1
240 jumpif multiple 300
250 set bottles " bottle "
300 set line "Take one down and pass it around, " + x
310 set line line + bottles
320 set line line + "of beer on the wall."
330 print line
340 print ""
350 jump 100
400 print "Take one down and pass it around, no more bottles of beer on the wall."
410 print ""
420 print "No more bottles of beer on the wall, no more bottles of beer."
430 print "Go to the store and buy some more, 99 bottles of beer on the wall."
https://github.com/nrubin29/Pogo
method main:void
declare(integer,i,99)
while i > 0
print(i "bottles of beer on the wall")
math(i - 1) i
end
end main
i
并将其设置为等于99。然后,当i大于0时,我打印i bottles of beer on the wall
并减去i
。如果问题是我缺少一些歌词,则可以添加更多歌词。